bot.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. # Définir le client Discord et les gestionnaires d'événements.
  2. import discord
  3. from discord.ext import commands
  4. from config import CHATGPT_CHANNEL_ID, BOT_NAME, BOT_VERSION
  5. from logger import logger
  6. from history import load_conversation_history, conversation_history, add_to_conversation_history, save_conversation_history
  7. from openai_client import call_gpt4o_for_image_analysis, call_gpt4o_mini_with_analysis, call_openai_api
  8. from utils import has_text, read_text_file, encode_image_from_attachment
  9. from history import IMAGE_ANALYSIS_MARKER
  10. import json
  11. class MyDiscordClient(discord.Client):
  12. def __init__(self, intents):
  13. super().__init__(intents=intents)
  14. self.last_analysis_index = None
  15. self.messages_since_last_analysis = 0
  16. async def close(self):
  17. from openai_client import openai_client
  18. if openai_client is not None:
  19. await openai_client.close()
  20. await super().close()
  21. # Initialiser les intents
  22. intents = discord.Intents.default()
  23. intents.message_content = True # Activer l'intent pour les contenus de message
  24. # Initialiser le client Discord
  25. client_discord = MyDiscordClient(intents=intents)
  26. # Charger l'historique au démarrage
  27. load_conversation_history()
  28. @client_discord.event
  29. async def on_ready():
  30. logger.info(f'{BOT_NAME} connecté en tant que {client_discord.user}')
  31. if not conversation_history:
  32. logger.info("Aucun historique trouvé. L'historique commence vide.")
  33. try:
  34. # Utiliser fetch_channel au lieu de get_channel
  35. channel = await client_discord.fetch_channel(CHATGPT_CHANNEL_ID)
  36. if channel:
  37. embed = discord.Embed(
  38. title="Bot Démarré",
  39. description=f"🎉 {BOT_NAME} est en ligne ! Version {BOT_VERSION}",
  40. color=0x00ff00 # Vert
  41. )
  42. await channel.send(embed=embed)
  43. logger.info(f"Message de connexion envoyé dans le canal ID {CHATGPT_CHANNEL_ID}")
  44. except discord.NotFound:
  45. logger.error(f"Canal avec ID {CHATGPT_CHANNEL_ID} non trouvé.")
  46. except discord.Forbidden:
  47. logger.error(f"Permissions insuffisantes pour envoyer des messages dans le canal ID {CHATGPT_CHANNEL_ID}.")
  48. except discord.HTTPException as e:
  49. logger.error(f"Erreur lors de l'envoi du message de connexion : {e}")
  50. @client_discord.event
  51. async def on_message(message):
  52. global conversation_history
  53. # Vérifier si le message provient du canal autorisé
  54. if message.channel.id != CHATGPT_CHANNEL_ID:
  55. return
  56. # Ignorer les messages du bot lui-même
  57. if message.author == client_discord.user:
  58. return
  59. user_text = message.content.strip()
  60. image_data = None
  61. file_content = None
  62. attachment_filename = None
  63. # Vérifier si le message est la commande de réinitialisation
  64. if user_text.lower() == "!reset_history":
  65. # Vérifier si l'utilisateur a les permissions administratives
  66. if not message.author.guild_permissions.administrator:
  67. await message.channel.send("❌ Vous n'avez pas la permission d'utiliser cette commande.")
  68. return
  69. conversation_history.clear()
  70. save_conversation_history()
  71. await message.channel.send("✅ L'historique des conversations a été réinitialisé.")
  72. logger.info(f"Historique des conversations réinitialisé par {message.author}.")
  73. return # Arrêter le traitement du message après la réinitialisation
  74. # Extensions de fichiers autorisées
  75. allowed_extensions = ['.txt', '.py', '.html', '.css', '.js']
  76. # Variables pour stocker si le message contient une image et/ou un fichier
  77. has_image = False
  78. has_file = False
  79. # Vérifier s'il y a une pièce jointe
  80. if message.attachments:
  81. for attachment in message.attachments:
  82. # Vérifier si c'est un fichier avec une extension autorisée
  83. if any(attachment.filename.endswith(ext) for ext in allowed_extensions):
  84. file_content = await read_text_file(attachment)
  85. attachment_filename = attachment.filename
  86. break
  87. # Vérifier si c'est une image
  88. elif attachment.content_type in ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/tiff']:
  89. image_data = await encode_image_from_attachment(attachment, mode='high')
  90. break
  91. # Si une image est présente, la traiter
  92. if image_data:
  93. has_user_text = has_text(user_text)
  94. user_text_to_use = user_text if has_user_text else None
  95. # Étape 1 : Envoyer un message temporaire indiquant que l'image est en cours d'analyse
  96. temp_msg = await message.channel.send(f"*{BOT_NAME} observe l'image...*")
  97. try:
  98. # Étape 2 : GPT-4o analyse l'image, potentiellement guidée par le texte de l'utilisateur
  99. analysis = await call_gpt4o_for_image_analysis(image_data, user_text=user_text_to_use)
  100. if analysis:
  101. # Ajouter l'analyse à l'historique avant de réagir avec GPT-4o Mini
  102. analysis_message = {
  103. "role": "system",
  104. "content": f"{IMAGE_ANALYSIS_MARKER}{analysis}"
  105. }
  106. await add_to_conversation_history(analysis_message)
  107. # Étape 3 : GPT-4o Mini réagit à la question et à l'analyse
  108. reply = await call_gpt4o_mini_with_analysis(
  109. analysis,
  110. message.author.name,
  111. user_text,
  112. has_text=has_user_text,
  113. conversation_history=conversation_history
  114. )
  115. if reply:
  116. # Étape 4 : Supprimer le message temporaire
  117. await temp_msg.delete()
  118. # Étape 5 : Envoyer la réponse finale
  119. await message.channel.send(reply)
  120. # Ajouter le message utilisateur à l'historique
  121. if has_user_text:
  122. user_message_content = f"{user_text} (a posté une image.)"
  123. else:
  124. user_message_content = (
  125. "Une image a été postée, mais elle n'est pas disponible pour analyse directe. "
  126. "Veuillez vous baser uniquement sur l'analyse fournie."
  127. )
  128. user_message = {
  129. "role": "user",
  130. "content": user_message_content
  131. }
  132. # Ajouter le message utilisateur à l'historique
  133. await add_to_conversation_history(user_message)
  134. # Ajouter le message assistant à l'historique
  135. assistant_message = {
  136. "role": "assistant",
  137. "content": reply
  138. }
  139. await add_to_conversation_history(assistant_message)
  140. else:
  141. # Étape 4 : Supprimer le message temporaire en cas d'échec de génération de réponse
  142. await temp_msg.delete()
  143. await message.channel.send("Désolé, je n'ai pas pu générer une réponse.")
  144. else:
  145. # Étape 4 : Supprimer le message temporaire en cas d'échec d'analyse
  146. await temp_msg.delete()
  147. await message.channel.send("Désolé, je n'ai pas pu analyser l'image.")
  148. except Exception as e:
  149. # Étape 4 : Supprimer le message temporaire en cas d'erreur
  150. await temp_msg.delete()
  151. await message.channel.send("Une erreur est survenue lors du traitement de l'image.")
  152. logger.error(f"Error during image processing: {e}")
  153. # Après traitement de l'image, ne pas continuer
  154. return
  155. # Ajouter le contenu du fichier à la requête si présent
  156. if file_content:
  157. user_text += f"\nContenu du fichier {attachment_filename}:\n{file_content}"
  158. # Vérifier si le texte n'est pas vide après ajout du contenu du fichier
  159. if not has_text(user_text):
  160. return # Ne pas appeler l'API si le texte est vide
  161. # Appeler l'API OpenAI
  162. result = await call_openai_api(user_text, message.author.name, conversation_history, image_data)
  163. if result:
  164. reply = result.choices[0].message.content
  165. await message.channel.send(reply)
  166. # Ajouter le message utilisateur à l'historique
  167. user_message = {
  168. "role": "user",
  169. "content": user_text
  170. }
  171. await add_to_conversation_history(user_message)
  172. # Ajouter le message assistant à l'historique
  173. assistant_message = {
  174. "role": "assistant",
  175. "content": reply
  176. }
  177. await add_to_conversation_history(assistant_message)