This commit is contained in:
Ladebeze66 2025-03-21 16:27:36 +01:00
parent 59bafbadb3
commit ade575f825
2 changed files with 125 additions and 174 deletions

View File

@ -7,184 +7,148 @@ import shutil
from typing import Dict, List, Any, Optional, Tuple, Union, Set from typing import Dict, List, Any, Optional, Tuple, Union, Set
def is_odoobot_message(message: Dict[str, Any]) -> bool: def is_odoobot_author(message: Dict[str, Any]) -> bool:
""" """
Détecte si un message provient d'OdooBot ou d'un bot système. Vérifie si l'auteur du message est OdooBot ou un autre système.
Args: Args:
message: Dictionnaire du message à analyser message: Le message à vérifier
Returns: Returns:
True si le message est d'OdooBot, False sinon True si le message provient d'OdooBot, False sinon
""" """
if not message: # Vérifier le nom de l'auteur
return False if 'author_id' in message and isinstance(message['author_id'], list) and len(message['author_id']) > 1:
author_name = message['author_id'][1].lower()
if 'odoobot' in author_name or 'bot' in author_name or 'système' in author_name:
return True
# Vérifier par le nom de l'auteur # Vérifier le type de message (souvent les notifications système)
author_name = ""
if message.get('author_id') and isinstance(message.get('author_id'), list) and len(message.get('author_id')) > 1:
author_name = message.get('author_id')[1].lower()
elif message.get('author_details', {}).get('name'):
author_name = message.get('author_details', {}).get('name', '').lower()
if 'odoobot' in author_name or 'bot' in author_name or 'système' in author_name or 'system' in author_name:
return True
# Vérifier par le contenu du message (messages système typiques)
body = message.get('body', '').lower()
if body and isinstance(body, str):
system_patterns = [
r'assigné à',
r'assigned to',
r'étape changée',
r'stage changed',
r'créé automatiquement',
r'automatically created',
r'a modifié la date limite',
r'changed the deadline',
r'a ajouté une pièce jointe',
r'added an attachment'
]
for pattern in system_patterns:
if re.search(pattern, body, re.IGNORECASE):
return True
# Vérifier par le type de message/sous-type
if message.get('message_type') == 'notification': if message.get('message_type') == 'notification':
return True return True
subtype_name = "" # Vérifier le sous-type du message
if message.get('subtype_id') and isinstance(message.get('subtype_id'), list) and len(message.get('subtype_id')) > 1: if 'subtype_id' in message and isinstance(message['subtype_id'], list) and len(message['subtype_id']) > 1:
subtype_name = message.get('subtype_id')[1].lower() subtype = message['subtype_id'][1].lower()
elif message.get('subtype_details') and isinstance(message.get('subtype_details'), list) and len(message.get('subtype_details')) > 0: if 'notification' in subtype or 'system' in subtype:
subtype_name = message.get('subtype_details')[0].get('name', '').lower() return True
if subtype_name and ('notification' in subtype_name or 'system' in subtype_name): # Vérifier le contenu du message
return True if 'body' in message and isinstance(message['body'], str):
body = message['body'].lower()
system_patterns = [
'assigné à', 'étape changée', 'créé automatiquement',
'assigned to', 'stage changed', 'automatically created',
'updated', 'mis à jour', 'a modifié', 'changed'
]
for pattern in system_patterns:
if pattern in body:
return True
return False return False
def is_important_image(img_tag: Any, message_text: str) -> bool: def is_important_image(tag, message_text: str) -> bool:
""" """
Détermine si une image est importante ou s'il s'agit d'une image inutile (logo, signature, etc.). Détermine si une image est importante ou s'il s'agit d'un logo/signature.
Args: Args:
img_tag: Balise d'image BeautifulSoup tag: La balise d'image à analyser
message_text: Texte du message complet pour contexte message_text: Le texte complet du message pour contexte
Returns: Returns:
True si l'image semble importante, False sinon True si l'image semble importante, False sinon
""" """
# Vérifier les attributs de l'image # Vérifier les attributs de l'image
img_src = img_tag.get('src', '') src = tag.get('src', '')
img_alt = img_tag.get('alt', '') alt = tag.get('alt', '')
img_class = img_tag.get('class', '') title = tag.get('title', '')
img_style = img_tag.get('style', '') css_class = tag.get('class', '')
# Mots-clés indiquant des images inutiles # Patterns pour les images inutiles
useless_patterns = [ useless_img_patterns = [
'logo', 'signature', 'footer', 'header', 'separator', 'separateur', 'logo', 'signature', 'outlook', 'footer', 'header', 'icon',
'outlook', 'mail_signature', 'icon', 'emoticon', 'emoji', 'cid:', 'emoticon', 'emoji', 'cid:', 'pixel', 'spacer', 'vignette',
'pixel', 'spacer', 'vignette', 'footer', 'banner', 'banniere' 'banner', 'separator', 'decoration', 'mail_signature'
] ]
# Vérifier le src/alt/class pour les motifs inutiles # Vérifier si c'est une image inutile
for pattern in useless_patterns: for pattern in useless_img_patterns:
if (pattern in img_src.lower() or if (pattern in src.lower() or
pattern in img_alt.lower() or pattern in alt.lower() or
(isinstance(img_class, list) and any(pattern in c.lower() for c in img_class)) or pattern in title.lower() or
(isinstance(img_class, str) and pattern in img_class.lower()) or (css_class and any(pattern in c.lower() for c in css_class if isinstance(c, str)))):
pattern in img_style.lower()):
return False return False
# Vérifier les dimensions (logos et icônes sont souvent petits) # Vérifier la taille (les petites images sont souvent des icônes/logos)
width = img_tag.get('width', '') width = tag.get('width', '')
height = img_tag.get('height', '') height = tag.get('height', '')
# Convertir en entiers si possible
try: try:
width = int(width) if width and width.isdigit() else None width = int(width) if width and str(width).isdigit() else None
height = int(height) if height and height.isdigit() else None height = int(height) if height and str(height).isdigit() else None
except (ValueError, TypeError): if width and height and width <= 50 and height <= 50:
# Extraire les dimensions des attributs style si disponibles
if img_style:
width_match = re.search(r'width:[ ]*(\d+)', img_style)
height_match = re.search(r'height:[ ]*(\d+)', img_style)
width = int(width_match.group(1)) if width_match else None
height = int(height_match.group(1)) if height_match else None
# Images très petites sont souvent des éléments décoratifs
if width is not None and height is not None:
if width <= 50 and height <= 50: # Taille arbitraire pour les petites images
return False return False
except (ValueError, TypeError):
pass
# Rechercher des termes qui indiquent l'importance de l'image dans le texte du message # Vérifier si l'image est mentionnée dans le texte
importance_indicators = [ image_indicators = [
'capture', 'screenshot', 'image', 'photo', 'illustration', 'capture', 'screenshot', 'image', 'photo', 'illustration',
'pièce jointe', 'attachment', 'voir', 'regarder', 'ci-joint', 'voir', 'regarder', 'ci-joint', 'écran', 'erreur', 'problème',
'écran', 'erreur', 'problème', 'bug', 'issue' 'bug', 'pièce jointe', 'attachment', 'veuillez trouver'
] ]
for indicator in importance_indicators: for indicator in image_indicators:
if indicator in message_text.lower(): if indicator in message_text.lower():
return True return True
# Par défaut, considérer l'image comme importante si aucun des filtres ci-dessus ne s'applique # Par défaut, considérer les images qui ne sont pas clairement inutiles comme potentiellement importantes
return True return True
def find_relevant_attachments(message_text: str, attachments_info: List[Dict[str, Any]]) -> List[int]: def find_attachment_references(message_text: str, attachments_info: List[Dict[str, Any]]) -> List[int]:
""" """
Trouve les pièces jointes pertinentes mentionnées dans le message. Identifie les pièces jointes mentionnées dans le message.
Args: Args:
message_text: Texte du message message_text: Texte du message
attachments_info: Liste des informations sur les pièces jointes attachments_info: Informations sur les pièces jointes disponibles
Returns: Returns:
Liste des IDs des pièces jointes pertinentes Liste des IDs des pièces jointes pertinentes
""" """
relevant_ids = []
if not message_text or not attachments_info: if not message_text or not attachments_info:
return relevant_ids return []
# Rechercher les mentions de pièces jointes dans le texte # Patterns indiquant des pièces jointes
attachment_indicators = [ attachment_indicators = [
r'pi(è|e)ce(s)? jointe(s)?', r'attachment(s)?', r'fichier(s)?', r'file(s)?', r'pi[èe]ce[s]? jointe[s]?', r'attachment[s]?', r'fichier[s]?', r'file[s]?',
r'voir (le|la|les) document(s)?', r'voir (le|la|les) fichier(s)?', r'veuillez trouver', r'please find', r'voir ci-joint', r'voir ci-dessous',
r'voir (le|la|les) image(s)?', r'voir (le|la|les) screenshot(s)?', r'ci-joint', r'joint[e]?[s]?', r'attached', r'screenshot[s]?',
r'voir (le|la|les) capture(s)?', r'voir (le|la|les) photo(s)?', r'capture[s]? d[\'e] ?[ée]cran', r'image[s]?', r'photo[s]?'
r'voir ci-joint', r'voir ci-dessous', r'voir ci-après',
r'veuillez trouver', r'please find', r'in attachment',
r'joint(e)?(s)?', r'attached', r'screenshot(s)?', r'capture(s)? d(\'|e) (é|e)cran',
r'image(s)?', r'photo(s)?'
] ]
has_attachment_mention = False relevant_ids = []
for indicator in attachment_indicators:
if re.search(indicator, message_text, re.IGNORECASE): # Vérifier si le message mentionne des pièces jointes
has_attachment_mention = True mention_found = False
for pattern in attachment_indicators:
if re.search(pattern, message_text, re.IGNORECASE):
mention_found = True
break break
# Si le message mentionne des pièces jointes if mention_found:
if has_attachment_mention: # Identifier les pièces jointes pertinentes (non logos/images d'interface)
for attachment in attachments_info: for attachment in attachments_info:
# Exclure les pièces jointes qui semblent être des signatures ou des logos name = attachment.get('name', '').lower() if attachment.get('name') else ''
name = attachment.get('name', '').lower()
useless_patterns = ['logo', 'signature', 'outlook', 'footer', 'header', 'icon', 'emoticon', 'emoji']
is_useless = False # Exclure les pièces jointes qui semblent être des logos ou images d'interface
for pattern in useless_patterns: useless_patterns = ['logo', 'signature', 'outlook', 'icon', 'emoticon', 'emoji']
if pattern in name: is_useless = any(pattern in name for pattern in useless_patterns)
is_useless = True
break
if not is_useless: if not is_useless and 'id' in attachment:
relevant_ids.append(attachment.get('id')) relevant_ids.append(attachment['id'])
return relevant_ids return relevant_ids
@ -192,7 +156,7 @@ def find_relevant_attachments(message_text: str, attachments_info: List[Dict[str
def clean_html(html_content: str) -> str: def clean_html(html_content: str) -> str:
""" """
Nettoie le contenu HTML en supprimant toutes les balises mais en préservant le texte. Nettoie le contenu HTML en supprimant toutes les balises mais en préservant le texte.
Améliore le traitement des images, supprime les signatures et les éléments inutiles. Traite spécifiquement les images pour garder uniquement celles pertinentes.
Args: Args:
html_content: Contenu HTML à nettoyer html_content: Contenu HTML à nettoyer
@ -206,41 +170,38 @@ def clean_html(html_content: str) -> str:
# Utiliser BeautifulSoup pour manipuler le HTML # Utiliser BeautifulSoup pour manipuler le HTML
soup = BeautifulSoup(html_content, 'html.parser') soup = BeautifulSoup(html_content, 'html.parser')
# Supprimer les signatures et pieds de courriels typiques # Supprimer les éléments de signature
signature_selectors = [ signature_elements = [
'div.signature', '.gmail_signature', '.signature', 'div.signature', '.gmail_signature', '.signature',
'hr + div', 'hr + p', '.footer', '.mail-signature', 'hr + div', 'hr + p', '.footer', '.mail-signature'
'.ms-signature', '[data-smartmail="gmail_signature"]'
] ]
for selector in signature_selectors: for selector in signature_elements:
for element in soup.select(selector): for element in soup.select(selector):
element.decompose() element.decompose()
# Supprimer les lignes horizontales qui séparent souvent les signatures # Supprimer les lignes horizontales (souvent utilisées pour séparer les signatures)
for hr in soup.find_all('hr'): for hr in soup.find_all('hr'):
hr.decompose() hr.decompose()
# Récupérer le texte complet pour analyse
full_text = soup.get_text(' ', strip=True)
# Traiter les images # Traiter les images
message_text = soup.get_text()
for img in soup.find_all('img'): for img in soup.find_all('img'):
if is_important_image(img, message_text): if is_important_image(img, full_text):
# Remplacer les images importantes par une description # Remplacer les images importantes par une description
alt_text = img.get('alt', '') or img.get('title', '') or '[Image importante]' alt_text = img.get('alt', '') or img.get('title', '') or '[Image importante]'
img.replace_with(f" [Image: {alt_text}] ") img.replace_with(f" [Image: {alt_text}] ")
else: else:
# Supprimer les images inutiles # Supprimer les images non pertinentes
img.decompose() img.decompose()
# Traiter les références aux pièces jointes # Traiter les liens vers des pièces jointes
attachment_refs = soup.find_all('a', href=re.compile(r'attachment|piece|fichier|file', re.IGNORECASE)) for a in soup.find_all('a', href=True):
for ref in attachment_refs: href = a.get('href', '').lower()
ref.replace_with(f" [Pièce jointe: {ref.get_text()}] ") if 'attachment' in href or 'download' in href or 'file' in href:
a.replace_with(f" [Pièce jointe: {a.get_text()}] ")
# Filtrer les éléments vides ou non significatifs
for tag in soup.find_all(['span', 'div', 'p']):
if not tag.get_text(strip=True):
tag.decompose()
# Récupérer le texte sans balises HTML # Récupérer le texte sans balises HTML
text = soup.get_text(separator=' ', strip=True) text = soup.get_text(separator=' ', strip=True)
@ -254,7 +215,7 @@ def clean_html(html_content: str) -> str:
# Nettoyer les lignes vides multiples # Nettoyer les lignes vides multiples
text = re.sub(r'\n\s*\n', '\n\n', text) text = re.sub(r'\n\s*\n', '\n\n', text)
# Supprimer les footers typiques des emails # Supprimer les disclaimers et signatures standards
footer_patterns = [ footer_patterns = [
r'Sent from my .*', r'Sent from my .*',
r'Envoyé depuis mon .*', r'Envoyé depuis mon .*',
@ -267,12 +228,7 @@ def clean_html(html_content: str) -> str:
r'This message and any attachments.*', r'This message and any attachments.*',
r'Ce message et ses pièces jointes.*', r'Ce message et ses pièces jointes.*',
r'AVIS DE CONFIDENTIALITÉ.*', r'AVIS DE CONFIDENTIALITÉ.*',
r'PRIVACY NOTICE.*', r'PRIVACY NOTICE.*'
r'Droit à la déconnexion.*',
r'L\'objectif du Support Technique.*',
r'\\*\\*\\*\\*\\*\\* ATTENTION \\*\\*\\*\\*\\*\\*.*',
r'Please consider the environment.*',
r'Pensez à l\'environnement.*'
] ]
for pattern in footer_patterns: for pattern in footer_patterns:
@ -288,14 +244,14 @@ def process_message_file(message_file_path: str, output_dir: str, attachments_in
Args: Args:
message_file_path: Chemin du fichier de message à traiter message_file_path: Chemin du fichier de message à traiter
output_dir: Répertoire de sortie pour le fichier traité output_dir: Répertoire de sortie pour le fichier traité
attachments_info: Informations sur les pièces jointes (optionnel) attachments_info: Informations sur les pièces jointes disponibles
""" """
try: try:
with open(message_file_path, 'r', encoding='utf-8') as f: with open(message_file_path, 'r', encoding='utf-8') as f:
message_data = json.load(f) message_data = json.load(f)
# Ignorer les messages d'OdooBot # Ignorer les messages d'OdooBot
if is_odoobot_message(message_data): if is_odoobot_author(message_data):
print(f"Message ignoré (OdooBot): {os.path.basename(message_file_path)}") print(f"Message ignoré (OdooBot): {os.path.basename(message_file_path)}")
return return
@ -304,9 +260,9 @@ def process_message_file(message_file_path: str, output_dir: str, attachments_in
# Remplacer le contenu HTML par le texte filtré # Remplacer le contenu HTML par le texte filtré
message_data['body'] = clean_html(message_data['body']) message_data['body'] = clean_html(message_data['body'])
# Identifier les pièces jointes pertinentes si disponibles # Identifier les pièces jointes pertinentes mentionnées dans le message
if attachments_info and message_data['body']: if attachments_info and message_data['body']:
relevant_attachments = find_relevant_attachments(message_data['body'], attachments_info) relevant_attachments = find_attachment_references(message_data['body'], attachments_info)
if relevant_attachments: if relevant_attachments:
message_data['relevant_attachment_ids'] = relevant_attachments message_data['relevant_attachment_ids'] = relevant_attachments
@ -328,48 +284,44 @@ def process_messages_threads(threads_file_path: str, output_dir: str, attachment
Args: Args:
threads_file_path: Chemin du fichier de threads de messages threads_file_path: Chemin du fichier de threads de messages
output_dir: Répertoire de sortie pour le fichier traité output_dir: Répertoire de sortie pour le fichier traité
attachments_info: Informations sur les pièces jointes (optionnel) attachments_info: Informations sur les pièces jointes disponibles
""" """
try: try:
with open(threads_file_path, 'r', encoding='utf-8') as f: with open(threads_file_path, 'r', encoding='utf-8') as f:
threads_data = json.load(f) threads_data = json.load(f)
# Stocker les IDs des threads à supprimer (qui ne contiennent que des messages d'OdooBot) # Liste des threads à supprimer (ceux qui ne contiennent que des messages d'OdooBot)
threads_to_remove = [] threads_to_remove = []
# Parcourir tous les threads # Parcourir tous les threads
for thread_id, thread in threads_data.items(): for thread_id, thread in threads_data.items():
# Traiter le message principal
# Vérifier si le message principal existe et n'est pas d'OdooBot
main_message_is_bot = False main_message_is_bot = False
if thread.get('main_message'): if thread.get('main_message'):
if is_odoobot_message(thread['main_message']): if is_odoobot_author(thread['main_message']):
main_message_is_bot = True main_message_is_bot = True
# Si c'est un message d'OdooBot, on le supprime
thread['main_message'] = None thread['main_message'] = None
elif 'body' in thread['main_message']: elif 'body' in thread['main_message']:
# Sinon, on nettoie le corps du message
thread['main_message']['body'] = clean_html(thread['main_message']['body']) thread['main_message']['body'] = clean_html(thread['main_message']['body'])
# Identifier les pièces jointes pertinentes # Identifier les pièces jointes pertinentes
if attachments_info and thread['main_message']['body']: if attachments_info and thread['main_message']['body']:
relevant_attachments = find_relevant_attachments( relevant_attachments = find_attachment_references(
thread['main_message']['body'], attachments_info thread['main_message']['body'], attachments_info
) )
if relevant_attachments: if relevant_attachments:
thread['main_message']['relevant_attachment_ids'] = relevant_attachments thread['main_message']['relevant_attachment_ids'] = relevant_attachments
# Filtrer les réponses pour supprimer celles d'OdooBot # Traiter les réponses (filtrer les messages d'OdooBot)
filtered_replies = [] filtered_replies = []
for reply in thread.get('replies', []): for reply in thread.get('replies', []):
if not is_odoobot_message(reply): if not is_odoobot_author(reply):
# Nettoyer le corps du message
if 'body' in reply: if 'body' in reply:
reply['body'] = clean_html(reply['body']) reply['body'] = clean_html(reply['body'])
# Identifier les pièces jointes pertinentes # Identifier les pièces jointes pertinentes
if attachments_info and reply['body']: if attachments_info and reply['body']:
relevant_attachments = find_relevant_attachments(reply['body'], attachments_info) relevant_attachments = find_attachment_references(reply['body'], attachments_info)
if relevant_attachments: if relevant_attachments:
reply['relevant_attachment_ids'] = relevant_attachments reply['relevant_attachment_ids'] = relevant_attachments
@ -378,8 +330,7 @@ def process_messages_threads(threads_file_path: str, output_dir: str, attachment
# Mettre à jour les réponses # Mettre à jour les réponses
thread['replies'] = filtered_replies thread['replies'] = filtered_replies
# Si le thread ne contient plus de messages (tous étaient des messages d'OdooBot), # Si le thread ne contient que des messages de bot, le marquer pour suppression
# marquer pour suppression
if main_message_is_bot and not filtered_replies: if main_message_is_bot and not filtered_replies:
threads_to_remove.append(thread_id) threads_to_remove.append(thread_id)
@ -394,7 +345,7 @@ def process_messages_threads(threads_file_path: str, output_dir: str, attachment
print(f"Fichier de threads traité: {os.path.basename(threads_file_path)}") print(f"Fichier de threads traité: {os.path.basename(threads_file_path)}")
if threads_to_remove: if threads_to_remove:
print(f" {len(threads_to_remove)} threads supprimés (messages d'OdooBot uniquement)") print(f" {len(threads_to_remove)} threads supprimés (OdooBot uniquement)")
except Exception as e: except Exception as e:
print(f"Erreur lors du traitement du fichier {threads_file_path}: {e}") print(f"Erreur lors du traitement du fichier {threads_file_path}: {e}")
@ -407,7 +358,7 @@ def process_messages_collection(messages_file_path: str, output_dir: str, attach
Args: Args:
messages_file_path: Chemin du fichier de collection de messages messages_file_path: Chemin du fichier de collection de messages
output_dir: Répertoire de sortie pour le fichier traité output_dir: Répertoire de sortie pour le fichier traité
attachments_info: Informations sur les pièces jointes (optionnel) attachments_info: Informations sur les pièces jointes disponibles
""" """
try: try:
with open(messages_file_path, 'r', encoding='utf-8') as f: with open(messages_file_path, 'r', encoding='utf-8') as f:
@ -416,14 +367,14 @@ def process_messages_collection(messages_file_path: str, output_dir: str, attach
# Filtrer les messages pour supprimer ceux d'OdooBot # Filtrer les messages pour supprimer ceux d'OdooBot
filtered_messages = [] filtered_messages = []
for message in messages_data: for message in messages_data:
if not is_odoobot_message(message): if not is_odoobot_author(message):
# Nettoyer le corps du message # Nettoyer le contenu HTML
if 'body' in message: if 'body' in message:
message['body'] = clean_html(message['body']) message['body'] = clean_html(message['body'])
# Identifier les pièces jointes pertinentes # Identifier les pièces jointes pertinentes
if attachments_info and message['body']: if attachments_info and message['body']:
relevant_attachments = find_relevant_attachments(message['body'], attachments_info) relevant_attachments = find_attachment_references(message['body'], attachments_info)
if relevant_attachments: if relevant_attachments:
message['relevant_attachment_ids'] = relevant_attachments message['relevant_attachment_ids'] = relevant_attachments
@ -464,7 +415,7 @@ def process_ticket_folder(ticket_folder: str, output_base_dir: str) -> None:
shutil.copy2(src_file, dst_file) shutil.copy2(src_file, dst_file)
print(f"Fichier copié: {file_name}") print(f"Fichier copié: {file_name}")
# Charger les informations sur les pièces jointes si disponibles # Charger les informations sur les pièces jointes
attachments_info = [] attachments_info = []
attachments_info_file = os.path.join(ticket_folder, 'attachments_info.json') attachments_info_file = os.path.join(ticket_folder, 'attachments_info.json')
if os.path.exists(attachments_info_file): if os.path.exists(attachments_info_file):
@ -502,7 +453,7 @@ def process_ticket_folder(ticket_folder: str, output_base_dir: str) -> None:
if os.path.exists(message_threads_file): if os.path.exists(message_threads_file):
process_messages_threads(message_threads_file, output_ticket_dir, attachments_info) process_messages_threads(message_threads_file, output_ticket_dir, attachments_info)
# Copier le répertoire des pièces jointes (on conserve toutes les pièces jointes) # Copier le répertoire des pièces jointes (on garde toutes les pièces jointes)
src_attachments_dir = os.path.join(ticket_folder, 'attachments') src_attachments_dir = os.path.join(ticket_folder, 'attachments')
if os.path.exists(src_attachments_dir): if os.path.exists(src_attachments_dir):
dst_attachments_dir = os.path.join(output_ticket_dir, 'attachments') dst_attachments_dir = os.path.join(output_ticket_dir, 'attachments')
@ -556,9 +507,9 @@ def run_filter_wizard() -> None:
print("\n==== FILTRAGE DES MESSAGES DES TICKETS ====") print("\n==== FILTRAGE DES MESSAGES DES TICKETS ====")
print("Cette fonction va:") print("Cette fonction va:")
print("1. Supprimer les messages provenant d'OdooBot") print("1. Supprimer les messages provenant d'OdooBot")
print("2. Supprimer les logos, signatures et images non pertinentes") print("2. Filtrer les images inutiles (logos, signatures, images Outlook)")
print("3. Conserver uniquement le texte utile des messages") print("3. Conserver les images pertinentes pour la demande")
print("4. Identifier les pièces jointes mentionnées dans les messages\n") print("4. Identifier les pièces jointes importantes mentionnées dans les messages\n")
# Demander le répertoire source # Demander le répertoire source
default_source = 'exported_tickets' default_source = 'exported_tickets'