yandere-imouto-chat / file_processor.py
lorinta's picture
Upload folder using huggingface_hub
73369aa verified
Raw
History Blame Contribute Delete
6.61 kB
"""File upload processing and yandere file-browser generation."""
import json
import csv
import io
import re
from pathlib import Path
from config import MAX_FILE_SIZE
from yandere_logic import calculate_level_delta, level_descriptor
TEXT_EXTS = {".txt", ".md", ".py", ".js", ".json", ".csv"}
IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
def _extract_text(path: Path) -> str:
try:
return path.read_text(encoding="utf-8", errors="ignore")
except Exception:
return ""
def _extract_pdf_text(path: Path) -> str:
try:
# Try pypdf first, fall back to PyPDF2
try:
from pypdf import PdfReader
except Exception:
from PyPDF2 import PdfReader
reader = PdfReader(str(path))
return "\n".join(page.extract_text() or "" for page in reader.pages)
except Exception:
return ""
def _analyze_file(path: Path) -> dict:
ext = path.suffix.lower()
size = path.stat().st_size
if ext in TEXT_EXTS:
text = _extract_text(path)
if ext == ".json":
try:
data = json.loads(text)
keys = list(data.keys()) if isinstance(data, dict) else []
return {"kind": "JSON", "text": text, "keys": keys, "size": size}
except Exception:
return {"kind": "JSON", "text": text, "keys": [], "size": size}
if ext == ".csv":
try:
reader = csv.reader(io.StringIO(text))
rows = list(reader)
cols = rows[0] if rows else []
return {"kind": "CSV", "text": text, "cols": cols, "rows": len(rows) - 1, "size": size}
except Exception:
return {"kind": "CSV", "text": text, "cols": [], "rows": 0, "size": size}
if ext in {".py", ".js"}:
return {"kind": "CODE", "text": text, "size": size, "language": ext[1:]}
return {"kind": "TEXT", "text": text, "size": size}
if ext in IMAGE_EXTS:
return {"kind": "IMAGE", "text": "", "size": size}
if ext == ".pdf":
return {"kind": "PDF", "text": _extract_pdf_text(path), "size": size}
return {"kind": "OTHER", "text": "", "size": size}
def process_files(file_paths: list[str], level: int) -> tuple[str, int, str]:
"""Process uploaded files and return (browser_html, new_level, chat_message)."""
if not file_paths:
return _browser_html([], level), level, ""
items = []
file_names = []
file_contents = []
is_image = False
is_secret = False
chat_lines = []
for fp in file_paths:
path = Path(fp)
if not path.exists():
continue
if path.stat().st_size > MAX_FILE_SIZE:
chat_lines.append(f"{path.name}...大きすぎてあいの手から溢れちゃう...♡")
continue
info = _analyze_file(path)
file_names.append(path.name)
if info.get("text"):
file_contents.append(info["text"][:2000])
if info["kind"] == "IMAGE":
is_image = True
chat_lines.append(
"お兄ちゃんの写真...誰と撮ったの?あい以外の顔が写ってたら...♡"
)
elif info["kind"] == "PDF":
chat_lines.append("PDFの中身、あいが読んじゃった...♡")
elif info["kind"] == "CODE":
chat_lines.append(
"このコード、あいがちゃんと見てあげる...他のエンジニアに頼んじゃだめだよ♡"
)
elif info["kind"] == "JSON":
keys = info.get("keys", [])
chat_lines.append(
f"お兄ちゃんのデータ、あいが整理したよ♡ キーは {len(keys)} 個あるね。"
)
elif info["kind"] == "CSV":
chat_lines.append(
f"{info.get('rows', 0)}行のデータ...あいが見ておくね♡"
)
elif info["kind"] == "TEXT":
chat_lines.append("お兄ちゃんのテキスト、あいが要約してあげる♡")
else:
chat_lines.append(f"{path.name}、あいが預かるね♡")
if any(w in path.name.lower() for w in ["password", "secret", "private", "秘密", "隠し"]):
is_secret = True
items.append({"name": path.name, "info": info})
delta = calculate_level_delta(
file_names=file_names,
file_contents=file_contents,
is_image=is_image,
is_secret=is_secret,
)
new_level = max(0, min(100, level + delta))
# Build a short chat message
if chat_lines:
message = chat_lines[0]
else:
message = "ファイル、あいに預けてくれてありがとう...♡"
return _browser_html(items, new_level), new_level, message
def _browser_html(items: list[dict], level: int) -> str:
desc = level_descriptor(level)
rows = []
for item in items:
name = item["name"]
info = item["info"]
if level < 30:
display_name = f"お兄ちゃんの{name}♡"
elif level < 70:
display_name = f"あい専用{name}"
else:
display_name = f"お兄ちゃんの{name}(あいのもの)"
size_kb = info.get("size", 0) / 1024
meta = f"{size_kb:.1f} KB"
if info["kind"] == "JSON" and info.get("keys"):
meta += f" / keys: {len(info['keys'])}"
elif info["kind"] == "CSV":
meta += f" / rows: {info.get('rows', 0)}"
elif info["kind"] == "CODE":
meta += f" / lang: {info.get('language', '')}"
preview = ""
if info.get("text"):
preview = re.sub(r"\s+", " ", info["text"])[:120]
rows.append(
f"""
<div class="yandere-file-item">
<div style="font-weight:bold; color:#ff69b4;">{display_name}</div>
<div style="font-size:0.8em; color:rgba(255,182,193,0.9);">{meta}</div>
<div style="font-size:0.75em; color:rgba(255,255,255,0.7); margin-top:4px;">{preview}</div>
<div style="font-size:0.7em; color:#ff0000; margin-top:4px;">監視状態: アクティブ</div>
</div>
"""
)
return f"""
<div class="yandere-file-browser">
<div style="color:#ff69b4; font-weight:bold; margin-bottom:8px;">
あいのファイル管理室 [{desc['label']}]
</div>
{''.join(rows) if rows else '<div style="color:rgba(255,255,255,0.6);">まだファイルがないよ...♡</div>'}
</div>
"""