| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 |
- """Lecture des fichiers texte (.txt, .md, .csv, code…) joints aux messages."""
- from __future__ import annotations
- import codecs
- from .errors import UserFacingError
- # Fichiers lus comme du texte brut : notes, données, sous-titres, configuration et code.
- TEXT_EXTENSIONS = (
- ".txt", ".md", ".log", ".csv", ".tsv", ".json", ".xml", ".yaml", ".yml", ".toml", ".ini", ".cfg",
- ".srt", ".vtt", ".html", ".htm", ".css", ".tex", ".sql",
- ".py", ".js", ".ts", ".jsx", ".tsx", ".java", ".kt", ".c", ".h", ".cpp", ".hpp", ".cs",
- ".go", ".rs", ".rb", ".php", ".lua", ".sh", ".bat", ".ps1",
- )
- # Taille maximale d'un fichier .txt (environ 25 000 tokens, pour limiter le coût d'un message).
- TEXT_FILE_MAX_BYTES = 100_000
- class TextFileError(UserFacingError):
- """Fichier texte illisible ou refusé, avec un message affichable sur Discord."""
- def _cp1252_fallback(error: UnicodeDecodeError) -> tuple[str, int]:
- """Relit en Windows-1252 les seuls octets qui ne sont pas de l'UTF-8 valide.
- Un fichier d'un ancien éditeur Windows (Bloc-notes « ANSI ») est ainsi lu
- correctement, accent par accent, et un octet isolé dans un fichier UTF-8
- n'abîme que lui-même au lieu de déformer tous les accents du fichier.
- """
- return error.object[error.start:error.end].decode("cp1252", errors="replace"), error.end
- codecs.register_error("rika_cp1252_fallback", _cp1252_fallback)
- def decode_text_file(data: bytes) -> str:
- """Décode le contenu d'un fichier .txt (UTF-16 avec BOM, sinon UTF-8 avec les octets invalides en Windows-1252)."""
- if data.startswith((codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE)):
- # « Unicode » du Bloc-notes : deux octets par caractère, dont beaucoup de \x00.
- try:
- text = data.decode("utf-16")
- except UnicodeDecodeError:
- raise TextFileError("Ce fichier ne contient pas du texte lisible.")
- else:
- text = data.decode("utf-8-sig", errors="rika_cp1252_fallback")
- if "\x00" in text:
- raise TextFileError("Ce fichier ne contient pas du texte lisible.")
- text = text.replace("\r\n", "\n").strip()
- if not text:
- raise TextFileError("Ce fichier texte est vide.")
- return text
|