text_files.py 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. """Lecture des fichiers texte (.txt, .md, .csv, code…) joints aux messages."""
  2. from __future__ import annotations
  3. import codecs
  4. # Fichiers lus comme du texte brut : notes, données, sous-titres, configuration et code.
  5. TEXT_EXTENSIONS = (
  6. ".txt", ".md", ".log", ".csv", ".tsv", ".json", ".xml", ".yaml", ".yml", ".toml", ".ini", ".cfg",
  7. ".srt", ".vtt", ".html", ".htm", ".css", ".tex", ".sql",
  8. ".py", ".js", ".ts", ".jsx", ".tsx", ".java", ".kt", ".c", ".h", ".cpp", ".hpp", ".cs",
  9. ".go", ".rs", ".rb", ".php", ".lua", ".sh", ".bat", ".ps1",
  10. )
  11. # Taille maximale d'un fichier .txt (environ 25 000 tokens, pour limiter le coût d'un message).
  12. TEXT_FILE_MAX_BYTES = 100_000
  13. class TextFileError(Exception):
  14. """Fichier texte illisible ou refusé, avec un message affichable sur Discord."""
  15. def __init__(self, user_message: str):
  16. super().__init__(user_message)
  17. self.user_message = user_message
  18. def decode_text_file(data: bytes) -> str:
  19. """Décode le contenu d'un fichier .txt (UTF-16 avec BOM, UTF-8, sinon Windows-1252)."""
  20. if data.startswith((codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE)):
  21. # « Unicode » du Bloc-notes : deux octets par caractère, dont beaucoup de \x00.
  22. try:
  23. text = data.decode("utf-16")
  24. except UnicodeDecodeError:
  25. raise TextFileError("Ce fichier ne contient pas du texte lisible.")
  26. else:
  27. try:
  28. text = data.decode("utf-8-sig")
  29. except UnicodeDecodeError:
  30. # Fichiers enregistrés par d'anciens éditeurs Windows (Bloc-notes, etc.).
  31. text = data.decode("cp1252", errors="replace")
  32. if "\x00" in text:
  33. raise TextFileError("Ce fichier ne contient pas du texte lisible.")
  34. text = text.replace("\r\n", "\n").strip()
  35. if not text:
  36. raise TextFileError("Ce fichier texte est vide.")
  37. return text