i want to make a chat who can make action can you patch this and show me the new full updated code :
import os
import datetime
import json
import asyncio
from dateutil.parser import parse as parse_date
import dateparser
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from email.mime.text import MIMEText
import base64

from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse
from fastapi.middleware.cors import CORSMiddleware

# Importer le client Groq
from groq import Groq

# Importer dotenv pour gérer les variables d'environnement
from dotenv import load_dotenv

load_dotenv()

# Importer GoogleAuthRequest pour éviter les conflits avec FastAPI's Request
from google.auth.transport.requests import Request as GoogleAuthRequest

# Définir les scopes pour les autorisations Google API
SCOPES = [
    'https://www.googleapis.com/auth/documents',
    'https://www.googleapis.com/auth/calendar',
    'https://www.googleapis.com/auth/drive',
    'https://www.googleapis.com/auth/gmail.send',
    'https://www.googleapis.com/auth/gmail.readonly'
]

app = FastAPI()

templates = Jinja2Templates(directory="templates")

# Ajouter le middleware CORS (si nécessaire)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Ajustez selon vos besoins
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

def authenticate_google_services():
    creds = None
    # Vérifier si les crédentiels existent déjà
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    # Si pas de crédentiels valides, initier le flux d'authentification
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(GoogleAuthRequest())
        else:
            if not os.path.exists('credentials.json'):
                print("Le fichier 'credentials.json' est manquant.")
                return None, None, None, None
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Sauvegarder les crédentiels pour la prochaine fois
        with open('token.json', 'w') as token:
            token.write(creds.to_json())
    # Créer les services Google Docs, Calendar, Drive, et Gmail
    try:
        docs_service = build('docs', 'v1', credentials=creds)
        calendar_service = build('calendar', 'v3', credentials=creds)
        drive_service = build('drive', 'v3', credentials=creds)
        gmail_service = build('gmail', 'v1', credentials=creds)
        return docs_service, calendar_service, drive_service, gmail_service
    except Exception as e:
        print(f"Une erreur s'est produite lors de la création des services Google : {e}")
        return None, None, None, None

# Initialiser les services Google
docs_service, calendar_service, drive_service, gmail_service = authenticate_google_services()
if not docs_service or not calendar_service or not drive_service or not gmail_service:
    print("Authentification échouée.")

# Obtenir la clé API Groq à partir des variables d'environnement
groq_api_key = os.getenv("GROQ_API_KEY")
if not groq_api_key:
    raise Exception("Veuillez définir la variable d'environnement 'GROQ_API_KEY' avec votre clé API Groq.")

# Initialiser le client Groq
groq_client = Groq(api_key=groq_api_key)

@app.get("/", response_class=HTMLResponse)
async def get_index(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    try:
        # Initialiser l'historique de la conversation
        conversation_history = []
        while True:
            # Recevoir le message de l'utilisateur
            data = await websocket.receive_text()
            request_data = json.loads(data)
            user_message = request_data.get('message')

            if not user_message:
                await websocket.send_text("Le champ 'message' est requis.")
                continue  # Continuer à écouter les prochains messages

            # Ajouter le message de l'utilisateur à l'historique
            conversation_history.append({"role": "user", "content": user_message})

            # Vérifier si une action doit être effectuée
            assistant_response = await process_user_request(user_message, conversation_history, websocket)

            # Ajouter la réponse de l'assistant à l'historique
            conversation_history.append({"role": "assistant", "content": assistant_response})

            # Envoyer la réponse à l'utilisateur
            await websocket.send_text(assistant_response)

    except WebSocketDisconnect:
        print("Client déconnecté")
    except Exception as e:
        await websocket.send_text(f"Erreur du serveur : {e}")

def generate_with_groq(conversation_history):
    response = groq_client.chat.completions.create(
        messages=conversation_history,
        model="llama3-8b-8192",
        temperature=0.7,
        max_tokens=512
    )
    return response.choices[0].message.content.strip()

def generate_simple_response(prompt):
    response = groq_client.chat.completions.create(
        messages=[{"role": "user", "content": prompt}],
        model="llama3-8b-8192",
        temperature=0.0,
        max_tokens=150
    )
    return response.choices[0].message.content.strip()

async def process_user_request(user_message, conversation_history, websocket: WebSocket):
    # Utiliser le LLM pour détecter l'action à effectuer
    detection_prompt = f"""Tu es un assistant qui analyse le message de l'utilisateur pour déterminer s'il y a une action à effectuer parmi les suivantes :
- creer_document : pour créer un document Google Docs.
- lire_calendrier : pour lire les événements du calendrier.
- creer_evenement_calendrier : pour créer un événement dans le calendrier.
- partager_document : pour partager un document avec quelqu'un.
- envoyer_email : pour envoyer un e-mail.
- lire_les_emails : pour lire les e-mails dans Gmail.

Pour chaque action, extrais les paramètres nécessaires sous la forme d'un objet JSON. Si aucune action n'est requise, réponds simplement par : {{"action": "chat"}}

Message de l'utilisateur : "{user_message}"

Réponds en JSON avec le format :
{{
  "action": "nom_de_l_action",
  "parameters": {{
    "paramètre1": "valeur1",
    "paramètre2": "valeur2",
    ...
  }}
}}
"""

    # Obtenir la réponse du LLM pour l'action à effectuer
    action_response = generate_simple_response(detection_prompt)

    try:
        action_data = json.loads(action_response)
        action = action_data.get("action", "chat")
    except json.JSONDecodeError:
        # Si le LLM n'a pas renvoyé un JSON valide, on suppose qu'il s'agit d'un chat normal
        action = "chat"

    if action == "chat":
        # Réponse normale de chat
        assistant_response = generate_with_groq(conversation_history)
        return assistant_response
    elif action == 'creer_document':
        return await create_document_from_parameters(action_data.get("parameters", {}), websocket)
    elif action == 'lire_calendrier':
        return await read_calendar_from_parameters(action_data.get("parameters", {}), websocket)
    elif action == 'creer_evenement_calendrier':
        return await create_calendar_event_from_parameters(action_data.get("parameters", {}), websocket)
    elif action == 'partager_document':
        return await share_document_from_parameters(action_data.get("parameters", {}), websocket)
    elif action == 'envoyer_email':
        return await send_email_from_parameters(action_data.get("parameters", {}), websocket)
    elif action == 'lire_les_emails':
        return await read_emails_from_parameters(action_data.get("parameters", {}), websocket)
    else:
        # Action non reconnue, on répond en chat normal
        assistant_response = generate_with_groq(conversation_history)
        return assistant_response

# Fonctions spécifiques aux actions (identiques à celles fournies auparavant)

async def create_document_from_parameters(parameters, websocket: WebSocket):
    if not docs_service:
        return "Le service Google Docs n'est pas disponible."
    try:
        title = parameters.get('title', 'Nouveau Document')
        content = parameters.get('content', '')

        if not content:
            return "Le contenu du document est vide."

        # Créer le document
        document = {'title': title}
        doc = docs_service.documents().create(body=document).execute()
        doc_id = doc.get('documentId')

        # Insérer le contenu
        requests_body = [
            {
                'insertText': {
                    'location': {'index': 1},
                    'text': content
                }
            }
        ]

        docs_service.documents().batchUpdate(
            documentId=doc_id,
            body={'requests': requests_body}
        ).execute()

        doc_url = f"https://docs.google.com/document/d/{doc_id}"
        return f"Document créé avec succès : {doc_url}"
    except Exception as e:
        return f"Une erreur s'est produite lors de la création du document : {e}"

async def read_calendar_from_parameters(parameters, websocket: WebSocket):
    if not calendar_service:
        return "Le service Google Calendar n'est pas disponible."
    try:
        start_date_str = parameters.get('start_date')
        end_date_str = parameters.get('end_date')

        # Convertir les dates
        if not start_date_str:
            start_date = datetime.datetime.utcnow()
            start_date_str = start_date.strftime('%Y-%m-%d')
        else:
            start_date = parse_date(start_date_str)

        if not end_date_str:
            end_date = start_date + datetime.timedelta(days=7)
            end_date_str = end_date.strftime('%Y-%m-%d')
        else:
            end_date = parse_date(end_date_str)

        # Formater les dates ISO
        start_date_iso = start_date.isoformat() + 'Z'
        end_date_iso = end_date.isoformat() + 'Z'

        # Obtenir les événements
        events_result = calendar_service.events().list(
            calendarId='primary', timeMin=start_date_iso, timeMax=end_date_iso,
            singleEvents=True, orderBy='startTime').execute()
        events = events_result.get('items', [])

        if not events:
            return "Aucun événement trouvé pour la période spécifiée."

        # Préparer la liste des événements
        event_list = []
        for event in events:
            start_event = event['start'].get('dateTime', event['start'].get('date'))
            summary = event.get('summary', 'Sans titre')
            event_list.append(f"{start_event} - {summary}")

        # Générer une réponse
        events_str = "\n".join(event_list)
        return f"Voici vos événements entre {start_date_str} et {end_date_str} :\n{events_str}"

    except Exception as e:
        return f"Une erreur s'est produite lors de la lecture du calendrier : {e}"

async def create_calendar_event_from_parameters(parameters, websocket: WebSocket):
    if not calendar_service:
        return "Le service Google Calendar n'est pas disponible."
    try:
        summary = parameters.get('summary', 'Nouvel Événement')
        description = parameters.get('description', '')
        location = parameters.get('location', '')
        start_datetime_str = parameters.get('start_datetime')
        end_datetime_str = parameters.get('end_datetime')
        time_zone = parameters.get('time_zone', 'UTC')

        if not start_datetime_str or not end_datetime_str:
            return "Les informations de date et heure sont manquantes."

        # Parser les dates
        start_datetime = dateparser.parse(start_datetime_str)
        end_datetime = dateparser.parse(end_datetime_str)

        if not start_datetime or not end_datetime:
            return "Impossible de parser les dates fournies."

        # Formater les dates ISO
        start_datetime_iso = start_datetime.isoformat()
        end_datetime_iso = end_datetime.isoformat()

        # Créer l'événement
        event = {
            'summary': summary,
            'location': location,
            'description': description,
            'start': {
                'dateTime': start_datetime_iso,
                'timeZone': time_zone,
            },
            'end': {
                'dateTime': end_datetime_iso,
                'timeZone': time_zone,
            },
        }

        event = calendar_service.events().insert(calendarId='primary', body=event).execute()
        event_link = event.get('htmlLink')
        return f"Événement créé avec succès : {event_link}"
    except Exception as e:
        return f"Impossible de créer l'événement : {e}"

async def share_document_from_parameters(parameters, websocket: WebSocket):
    if not drive_service:
        return "Le service Google Drive n'est pas disponible."
    try:
        file_name = parameters.get('file_name')
        user_email = parameters.get('user_email')

        if not file_name or not user_email:
            return "Les informations 'file_name' ou 'user_email' sont manquantes."

        # Rechercher le fichier par nom
        search_query = f"name = '{file_name}'"
        response = drive_service.files().list(q=search_query, spaces='drive').execute()
        files = response.get('files', [])

        if not files:
            return f"Aucun fichier trouvé avec le nom '{file_name}'."

        file_id = files[0].get('id')

        # Partager le fichier
        permission = {
            'type': 'user',
            'role': 'writer',
            'emailAddress': user_email
        }
        drive_service.permissions().create(
            fileId=file_id,
            body=permission,
            fields='id',
            sendNotificationEmail=True,
        ).execute()
        return f"Le fichier '{file_name}' a été partagé avec {user_email}."
    except Exception as e:
        return f"Impossible de partager le document : {e}"

async def send_email_from_parameters(parameters, websocket: WebSocket):
    if not gmail_service:
        return "Le service Gmail n'est pas disponible."
    try:
        to = parameters.get('to')
        subject = parameters.get('subject', '')
        message_text = parameters.get('message', '')

        if not to or not message_text:
            return "Les informations 'to' ou 'message' sont manquantes."

        # Envoyer l'e-mail
        message = MIMEText(message_text)
        message['to'] = to
        message['from'] = 'votre_adresse_email@example.com'  # Remplacez par votre adresse e-mail
        message['subject'] = subject
        raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
        body = {'raw': raw_message}
        gmail_service.users().messages().send(userId='me', body=body).execute()
        return f"E-mail envoyé à {to}."
    except Exception as e:
        return f"Impossible d'envoyer l'e-mail : {e}"

async def read_emails_from_parameters(parameters, websocket: WebSocket):
    if not gmail_service:
        return "Le service Gmail n'est pas disponible."
    try:
        query = parameters.get('query', 'is:unread')

        # Lire les e-mails correspondant à la requête
        response = gmail_service.users().messages().list(userId='me', q=query).execute()
        messages = response.get('messages', [])

        if not messages:
            return "Aucun e-mail trouvé."

        emails = []
        for message in messages[:5]:  # Limiter à 5 e-mails pour éviter une réponse trop longue
            msg = gmail_service.users().messages().get(userId='me', id=message['id'], format='full').execute()
            headers = msg.get('payload', {}).get('headers', [])
            subject = "Sans sujet"
            from_email = "Inconnu"
            for header in headers:
                if header['name'] == 'Subject':
                    subject = header['value']
                elif header['name'] == 'From':
                    from_email = header['value']
            snippet = msg.get('snippet', '')
            emails.append(f"De: {from_email}\nSujet: {subject}\nAperçu: {snippet}\n")

        email_list = "\n---\n".join(emails)
        return f"Voici les e-mails trouvés :\n{email_list}"
    except Exception as e:
        return f"Impossible de lire les e-mails : {e}"

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Assistant Intelligent</title>
    <style>
        body {
            margin: 0;
            font-family: Arial, sans-serif;
            background-color: #f4f4f9;
            color: #333;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
        }

        .container {
            width: 100%;
            max-width: 600px;
            background: #ffffff;
            border-radius: 8px;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
            display: flex;
            flex-direction: column;
            padding: 16px;
        }

        h1 {
            text-align: center;
            margin-bottom: 20px;
            color: #333;
        }

        #chat-container {
            flex-grow: 1;
            overflow-y: auto;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            padding: 16px;
            margin-bottom: 16px;
            background: #f9f9f9;
        }

        .message {
            margin-bottom: 12px;
            padding: 10px 14px;
            border-radius: 8px;
            font-size: 14px;
            line-height: 1.4;
        }

        .user-message {
            background: #e1f5fe;
            align-self: flex-end;
            color: #0277bd;
        }

        .assistant-message {
            background: #ede7f6;
            align-self: flex-start;
            color: #512da8;
        }

        form {
            display: flex;
            gap: 8px;
        }

        input[type="text"] {
            flex-grow: 1;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 8px;
            font-size: 16px;
        }

        button[type="submit"] {
            background: #6200ea;
            color: #fff;
            border: none;
            padding: 10px 20px;
            border-radius: 8px;
            font-size: 16px;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        button[type="submit"]:hover {
            background: #4500b0;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Assistant Intelligent</h1>
        <div id="chat-container"></div>
        <form id="assistant-form">
            <input type="text" id="user_request" name="user_request" placeholder="Tapez votre message ici..." autocomplete="off" required />
            <button type="submit" id="send-button">Envoyer</button>
        </form>
    </div>

    <script>
        const form = document.getElementById('assistant-form');
        const userRequestInput = document.getElementById('user_request');
        const chatContainer = document.getElementById('chat-container');

        let websocket;

        form.addEventListener('submit', function(event) {
            event.preventDefault();

            const userRequest = userRequestInput.value.trim();
            if (!userRequest) return;

            // Display user message
            const userMessageDiv = document.createElement('div');
            userMessageDiv.className = 'message user-message';
            userMessageDiv.innerHTML = `<p>${userRequest}</p>`;
            chatContainer.appendChild(userMessageDiv);
            chatContainer.scrollTop = chatContainer.scrollHeight;

            // Clear input
            userRequestInput.value = '';

            // Display assistant loading message
            const assistantMessageDiv = document.createElement('div');
            assistantMessageDiv.className = 'message assistant-message';
            assistantMessageDiv.innerHTML = `<p>Chargement...</p>`;
            chatContainer.appendChild(assistantMessageDiv);
            chatContainer.scrollTop = chatContainer.scrollHeight;

            // Open WebSocket connection
            const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
            websocket = new WebSocket(`${protocol}://${window.location.host}/ws`);

            websocket.onopen = function() {
                websocket.send(JSON.stringify({ message: userRequest }));
            };

            websocket.onmessage = function(event) {
                const data = event.data;
                assistantMessageDiv.innerHTML = `<p>${data}</p>`;
                chatContainer.scrollTop = chatContainer.scrollHeight;
            };

            websocket.onerror = function(error) {
                console.error('WebSocket Error:', error);
                assistantMessageDiv.innerHTML = '<p>Une erreur est survenue lors de la communication avec le serveur.</p>';
            };

            websocket.onclose = function() {
                console.log('WebSocket connection closed');
            };
        });
    </script>
</body>
</html>