miércoles, 12 de junio de 2024

Enviar correos con GMAIL(3). Python y la nueva seguridad de Google

Ver https://developers.google.com/gmail/api/quickstart/python

Repositorio GIT de código google para utilizar esta API


1. Permitir la API (Enable the API)

ir a https://console.cloud.google.com/apis/enableflow?apiid=gmail.googleapis.com y seleccionar el proyecto o crear uno nuevo

Si clicamos donde se indica podemos crear proyectos o modificar uno existente.

Le damos a SIGUIENTE

Y aparece esta pantalla y le damos a HABILITAR para habilitar la API



2. Configurar la pantalla de consentimiento de OAuth

Entrar en https://console.cloud.google.com/iam-admin

Añadir un usuario habilitado y su correo electronico con el botón "+ ADD USERS" y después seleccionarlo en el filtro


3. Autorizar las credenciales para una apliocación de escritorio

Entrar en https://console.cloud.google.com/apis/credentials 

Clicar el boton de "+ CREAR CREDENCIALES" y seleccionar "ID de Cliente de OAuth"


Y aparece esta pantalla donde seleccionamos App de escritorio y le damos un nombre


Y le damos al btón de CREAR

Y aparece esta pantalla



y podemos decargar el JSON y le damos a ceptar

El ficheo JSON es

{
"installed":{
"client_id":"*********************************************.apps.googleusercontent.com",
"project_id":"********-***-******",
"auth_uri":"https://accounts.google.com/o/oauth2/auth",
"token_uri":"https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs",
"client_secret":"******-********-*****************",
"redirect_uris":["http://localhost"]
}
}

Hay que guardar dicho fichero en el directorio de trabajo con el nombre de "credentials.json"

4. Instalar las librerías de cliente de Google

En nuestro caso nos vamos a la carpeta donde está el entorno de python:

En una ventana de comandos ejecutar:


#1.Cambiamos de directorio.

cd ~/MyOdoo/Control-Presencia #2.Instalamos los paquetes de Google indicando la ruta del ejecutable python en
#  el entorno virtual 
./bin/python -m pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib


5. Crear un programa Python para registrar las credenciales 

Ahora nos vamos al direcotrio de nuestro proyecto y creamos el fichero "quickstart.py".

que contendrá este código modificado por el tema de localización de ficheros dentro de la carpeta de trabajo.

Hay que tener cuidado con la variable SCOPES!!! como veremos mas adelante.

import os.path
import sys

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

# If modifying these scopes, delete the file token.json.
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]


def main():
  """Shows basic usage of the Gmail API.
  Lists the user's Gmail labels.
  """
  creds = None
  # The file token.json stores the user's access and refresh tokens, and is
  # created automatically when the authorization flow completes for the first
  # time.
  LOCAL_PATH= os.path.dirname(os.path.abspath(sys.argv[0])) 
  tokenFile=LOCAL_PATH+"/"+"token.json"
  if os.path.exists(tokenFile):
    creds = Credentials.from_authorized_user_file(tokenFile, SCOPES)
  # If there are no (valid) credentials available, let the user log in.
  if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
      creds.refresh(Request())
    else:
      credentialsFile=LOCAL_PATH+"/"+"credentials.json"
      flow = InstalledAppFlow.from_client_secrets_file(
          credentialsFile, SCOPES
      )
      creds = flow.run_local_server(port=0)
    # Save the credentials for the next run
    with open(tokenFile, "w") as token:
      token.write(creds.to_json())

  try:
    # Call the Gmail API
    service = build("gmail", "v1", credentials=creds)
    results = service.users().labels().list(userId="me").execute()
    labels = results.get("labels", [])

    if not labels:
      print("No labels found.")
      return
    print("Labels:")
    for label in labels:
      print(label["name"])

  except HttpError as error:
    # TODO(developer) - Handle errors from gmail API.
    print(f"An error occurred: {error}")


if __name__ == "__main__":
  main()


Y cuando lo ejecutmos por primera vez nos pedirá que aceptemos las condiciones etc.. y al final creará un fichero "token.json" en nuestra carpeta de trabajo que permitirá ejecutar el programa sin pedirlo mas veces.

6. Enviar un correo

Si no cambiamos la linea de SCOPES nos dará un error:

googleapi: Error 403: Request had insufficient authentication scopes.

Hay que tener en cuenta que en el programa anterior tenemos en la línea 11:

# If modifying these scopes, delete the file token.json.
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]

que indica claramente que solo podemos leer correos. Si queremos enviar hay que utiliar otros SCOPES. 

Los scopes vienen dados en https://developers.google.com/gmail/api/auth/scopes 

Para nuestro caso queremos enviar un correo, por tanto podemos cambiar la línea por esta otra

# If modifying these scopes, delete the file token.json.
SCOPES = ["https://www.googleapis.com/auth/gmail.send"]

y tenemos que borrar el fichero "token.json"

Aquí hay un programa simple que verifica si un fichero es mas antiguo de 0,25 dias y si lo es envia un correo a una cuenta.

import os.path as path
import sys
import base64
import time
from email.message import EmailMessage

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

# If modifying these scopes, delete the file token.json.
SCOPES = ["https://www.googleapis.com/auth/gmail.send"]


'''
Mail Utils
'''

#==============================================================================
# GENERAL
#==============================================================================
def getGMailCredentials()->object:
    creds = None
    # The file token.json stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    LOCAL_PATH= path.dirname(path.abspath(sys.argv[0])) 
    tokenFile=LOCAL_PATH+"/"+"token.json"
    if path.exists(tokenFile):
        creds = Credentials.from_authorized_user_file(tokenFile, SCOPES)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            credentialsFile=LOCAL_PATH+"/"+"credentials.json"
            flow = InstalledAppFlow.from_client_secrets_file(
                credentialsFile, SCOPES
            )
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open(tokenFile, "w") as token:
            token.write(creds.to_json())

    return creds
        
def sendGMail(senderEmail:str='ximodante@gmail.com', message:str='Prueba', receiversEmail:list[str]=['receiver@gmail.com']):
    """Create and send an email message
    Print the returned  message id
    Returns: Message object, including message id

    Load pre-authorized user credentials from the environment.
    TODO(developer) - See https://developers.google.com/identity
    for guides on implementing OAuth2 for the application.
    """
    #creds, _ = google.auth.default()
    creds=getGMailCredentials()

    try:
        service = build("gmail", "v1", credentials=creds)
        eMessage = EmailMessage()

        eMessage.set_content("This is automated draft mail")

        eMessage["To"] = receiversEmail[0]
        eMessage["From"] = senderEmail
        eMessage["Subject"] = message

        # encoded message
        encoded_message = base64.urlsafe_b64encode(eMessage.as_bytes()).decode()

        create_message = {"raw": encoded_message}
    # pylint: disable=E1101
        send_message = (
            service.users()
            .messages()
            .send(userId="me", body=create_message)
            .execute()
        )
        print(f'Message Id: {send_message["id"]}')
    
    except HttpError as error:
        print(f"An error occurred: {error}")
        send_message = None
    return send_message


#if __name__ == "__main__":
#  gmail_send_message()
# [END gmail_send_message]

   

def myTestCron():
    try:
        senderEmail=sys.args[1]      
        message=sys.args[2]
        receiversEmail=[sys.args[3]]
        filePath=sys.args[4]

        
    except:
        senderEmail='ximodante@gmail.com'  
        message='Ha fallat la importació del padró de OESIA'
        receiversEmail=['receiver@gmail.com'] #a010
        filePath='/mnt/NASDADES_DOCS/A0-003_INFORMATICA/ASS/ASS2024/AS2024-0042_Padro_habitants_policia/PH_ROLAN_ODOO.xlsx'
        #filePath='Z:\\A0-003_INFORMATICA\\ASS\\ASS2024\\AS2024-0042_Padro_habitants_policia\\PH_ROLAN_ODOO.xlsx'

    if path.exists(filePath):
        
        if (time.time() - path.getmtime(filePath)) / (3600  *24) > 1: 
            print('File older than 1 day')
            sendGMail(senderEmail=senderEmail, message=('Fitxer > 1 dia ' + message), receiversEmail=receiversEmail)
    else:
        sendGMail(senderEmail, ('No existeix el fitxer ' + message), receiversEmail)
#------ main
myTestCron()      


No hay comentarios :

Publicar un comentario