2026-04-19 15:22:49 +03:00
|
|
|
|
import os
|
2026-03-02 04:30:10 +03:00
|
|
|
|
import shutil
|
2026-04-19 15:22:49 +03:00
|
|
|
|
import platform
|
2026-03-02 04:30:10 +03:00
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
from datetime import datetime
|
2026-04-19 15:22:49 +03:00
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
import send2trash
|
|
|
|
|
|
_SEND2TRASH = True
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
|
_SEND2TRASH = False
|
|
|
|
|
|
|
|
|
|
|
|
_OS = platform.system() # "Windows" | "Darwin" | "Linux"
|
|
|
|
|
|
|
|
|
|
|
|
_SAFE_ROOTS: list[Path] = [
|
|
|
|
|
|
Path.home(),
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
def _is_safe_path(target: Path) -> bool:
|
|
|
|
|
|
"""Verilen path _SAFE_ROOTS içinde mi? Değilse işlemi reddet."""
|
|
|
|
|
|
try:
|
|
|
|
|
|
resolved = target.resolve()
|
|
|
|
|
|
return any(
|
|
|
|
|
|
resolved == root.resolve() or resolved.is_relative_to(root.resolve())
|
|
|
|
|
|
for root in _SAFE_ROOTS
|
|
|
|
|
|
)
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
return False
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
def _get_desktop() -> Path:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
if _OS == "Linux":
|
|
|
|
|
|
xdg = os.environ.get("XDG_DESKTOP_DIR", "")
|
|
|
|
|
|
if xdg and Path(xdg).exists():
|
|
|
|
|
|
return Path(xdg)
|
2026-03-02 04:30:10 +03:00
|
|
|
|
return Path.home() / "Desktop"
|
|
|
|
|
|
|
|
|
|
|
|
def _get_downloads() -> Path:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
if _OS == "Linux":
|
|
|
|
|
|
xdg = os.environ.get("XDG_DOWNLOAD_DIR", "")
|
|
|
|
|
|
if xdg and Path(xdg).exists():
|
|
|
|
|
|
return Path(xdg)
|
2026-03-02 04:30:10 +03:00
|
|
|
|
return Path.home() / "Downloads"
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def _get_documents() -> Path:
|
|
|
|
|
|
if _OS == "Linux":
|
|
|
|
|
|
xdg = os.environ.get("XDG_DOCUMENTS_DIR", "")
|
|
|
|
|
|
if xdg and Path(xdg).exists():
|
|
|
|
|
|
return Path(xdg)
|
|
|
|
|
|
return Path.home() / "Documents"
|
|
|
|
|
|
|
|
|
|
|
|
def _get_pictures() -> Path:
|
|
|
|
|
|
if _OS == "Linux":
|
|
|
|
|
|
xdg = os.environ.get("XDG_PICTURES_DIR", "")
|
|
|
|
|
|
if xdg and Path(xdg).exists():
|
|
|
|
|
|
return Path(xdg)
|
|
|
|
|
|
return Path.home() / "Pictures"
|
|
|
|
|
|
|
|
|
|
|
|
def _get_music() -> Path:
|
|
|
|
|
|
if _OS == "Linux":
|
|
|
|
|
|
xdg = os.environ.get("XDG_MUSIC_DIR", "")
|
|
|
|
|
|
if xdg and Path(xdg).exists():
|
|
|
|
|
|
return Path(xdg)
|
|
|
|
|
|
return Path.home() / "Music"
|
|
|
|
|
|
|
|
|
|
|
|
def _get_videos() -> Path:
|
|
|
|
|
|
if _OS == "Linux":
|
|
|
|
|
|
xdg = os.environ.get("XDG_VIDEOS_DIR", "")
|
|
|
|
|
|
if xdg and Path(xdg).exists():
|
|
|
|
|
|
return Path(xdg)
|
|
|
|
|
|
return Path.home() / "Videos"
|
|
|
|
|
|
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
def _resolve_path(raw: str) -> Path:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
shortcuts: dict[str, Path] = {
|
|
|
|
|
|
"desktop": _get_desktop(),
|
|
|
|
|
|
"downloads": _get_downloads(),
|
|
|
|
|
|
"documents": _get_documents(),
|
|
|
|
|
|
"pictures": _get_pictures(),
|
|
|
|
|
|
"music": _get_music(),
|
|
|
|
|
|
"videos": _get_videos(),
|
2026-03-02 04:30:10 +03:00
|
|
|
|
"home": Path.home(),
|
|
|
|
|
|
}
|
|
|
|
|
|
lower = raw.strip().lower()
|
|
|
|
|
|
if lower in shortcuts:
|
|
|
|
|
|
return shortcuts[lower]
|
|
|
|
|
|
return Path(raw).expanduser()
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def _format_size(b: int) -> str:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
for unit in ["B", "KB", "MB", "GB", "TB"]:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
if b < 1024:
|
|
|
|
|
|
return f"{b:.1f} {unit}"
|
|
|
|
|
|
b /= 1024
|
|
|
|
|
|
return f"{b:.1f} TB"
|
|
|
|
|
|
|
|
|
|
|
|
def _safe_trash(target: Path) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
if not _SEND2TRASH:
|
|
|
|
|
|
return (
|
|
|
|
|
|
"send2trash is not installed. "
|
|
|
|
|
|
"Run: pip install send2trash — "
|
|
|
|
|
|
"Permanent deletion is disabled for safety."
|
|
|
|
|
|
)
|
|
|
|
|
|
send2trash.send2trash(str(target))
|
|
|
|
|
|
return f"Moved to Trash: {target.name}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def list_files(path: str = "desktop", show_hidden: bool = False) -> str:
|
|
|
|
|
|
try:
|
|
|
|
|
|
target = _resolve_path(path)
|
2026-04-19 15:22:49 +03:00
|
|
|
|
if not _is_safe_path(target):
|
|
|
|
|
|
return f"Access denied: {target}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
if not target.exists():
|
|
|
|
|
|
return f"Path not found: {target}"
|
|
|
|
|
|
if not target.is_dir():
|
|
|
|
|
|
return f"Not a directory: {target}"
|
|
|
|
|
|
|
|
|
|
|
|
items = []
|
|
|
|
|
|
for item in sorted(target.iterdir()):
|
|
|
|
|
|
if not show_hidden and item.name.startswith("."):
|
|
|
|
|
|
continue
|
|
|
|
|
|
if item.is_dir():
|
|
|
|
|
|
items.append(f"📁 {item.name}/")
|
|
|
|
|
|
else:
|
|
|
|
|
|
size = _format_size(item.stat().st_size)
|
|
|
|
|
|
items.append(f"📄 {item.name} ({size})")
|
|
|
|
|
|
|
|
|
|
|
|
if not items:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return f"Directory is empty: {target.name}/"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
return f"Contents of {target.name}/ ({len(items)} items):\n" + "\n".join(items)
|
|
|
|
|
|
|
|
|
|
|
|
except PermissionError:
|
|
|
|
|
|
return f"Permission denied: {path}"
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Error listing files: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def create_file(path: str, name: str = "", content: str = "") -> str:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
try:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
base = _resolve_path(path)
|
|
|
|
|
|
target = (base / name) if name else base
|
|
|
|
|
|
if not _is_safe_path(target):
|
|
|
|
|
|
return f"Access denied: {target}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
target.write_text(content, encoding="utf-8")
|
|
|
|
|
|
return f"File created: {target.name}"
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Could not create file: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def create_folder(path: str, name: str = "") -> str:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
try:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
base = _resolve_path(path)
|
|
|
|
|
|
target = (base / name) if name else base
|
|
|
|
|
|
if not _is_safe_path(target):
|
|
|
|
|
|
return f"Access denied: {target}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
target.mkdir(parents=True, exist_ok=True)
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return f"Folder created: {target.name}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Could not create folder: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def delete_file(path: str, name: str = "") -> str:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
try:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
base = _resolve_path(path)
|
|
|
|
|
|
target = (base / name) if name else base
|
|
|
|
|
|
if not _is_safe_path(target):
|
|
|
|
|
|
return f"Access denied: {target}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
if not target.exists():
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return f"Not found: {target.name}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
# Güvenli dizin kontrolü — kritik kullanıcı klasörlerini koru
|
|
|
|
|
|
protected = {
|
|
|
|
|
|
_get_desktop(), _get_downloads(), _get_documents(),
|
|
|
|
|
|
_get_pictures(), _get_music(), _get_videos(), Path.home()
|
|
|
|
|
|
}
|
|
|
|
|
|
if target.resolve() in {p.resolve() for p in protected}:
|
|
|
|
|
|
return f"Protected directory, cannot delete: {target.name}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return _safe_trash(target)
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
except PermissionError:
|
|
|
|
|
|
return f"Permission denied: {path}"
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Could not delete: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def move_file(path: str, name: str = "", destination: str = "") -> str:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
try:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
base = _resolve_path(path)
|
|
|
|
|
|
src = (base / name) if name else base
|
|
|
|
|
|
dst = _resolve_path(destination) if destination else None
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
if not src.exists():
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return f"Source not found: {src.name}"
|
|
|
|
|
|
if dst is None:
|
|
|
|
|
|
return "No destination specified."
|
|
|
|
|
|
if not _is_safe_path(src):
|
|
|
|
|
|
return f"Access denied (source): {src}"
|
|
|
|
|
|
if not _is_safe_path(dst):
|
|
|
|
|
|
return f"Access denied (destination): {dst}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
if dst.is_dir():
|
|
|
|
|
|
dst = dst / src.name
|
|
|
|
|
|
|
|
|
|
|
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
shutil.move(str(src), str(dst))
|
|
|
|
|
|
return f"Moved: {src.name} → {dst.parent.name}/"
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Could not move: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def copy_file(path: str, name: str = "", destination: str = "") -> str:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
try:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
base = _resolve_path(path)
|
|
|
|
|
|
src = (base / name) if name else base
|
|
|
|
|
|
dst = _resolve_path(destination) if destination else None
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
if not src.exists():
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return f"Source not found: {src.name}"
|
|
|
|
|
|
if dst is None:
|
|
|
|
|
|
return "No destination specified."
|
|
|
|
|
|
if not _is_safe_path(src):
|
|
|
|
|
|
return f"Access denied (source): {src}"
|
|
|
|
|
|
if not _is_safe_path(dst):
|
|
|
|
|
|
return f"Access denied (destination): {dst}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
if dst.is_dir():
|
|
|
|
|
|
dst = dst / src.name
|
|
|
|
|
|
|
|
|
|
|
|
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
|
|
if src.is_dir():
|
|
|
|
|
|
shutil.copytree(str(src), str(dst))
|
|
|
|
|
|
else:
|
|
|
|
|
|
shutil.copy2(str(src), str(dst))
|
|
|
|
|
|
|
|
|
|
|
|
return f"Copied: {src.name} → {dst.parent.name}/"
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Could not copy: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def rename_file(path: str, name: str = "", new_name: str = "") -> str:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
try:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
base = _resolve_path(path)
|
|
|
|
|
|
target = (base / name) if name else base
|
|
|
|
|
|
if not _is_safe_path(target):
|
|
|
|
|
|
return f"Access denied: {target}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
if not target.exists():
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return f"Not found: {target.name}"
|
|
|
|
|
|
if not new_name:
|
|
|
|
|
|
return "No new name provided."
|
|
|
|
|
|
|
|
|
|
|
|
new_path = target.parent / new_name
|
2026-03-02 04:30:10 +03:00
|
|
|
|
if new_path.exists():
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return f"A file named '{new_name}' already exists here."
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
target.rename(new_path)
|
|
|
|
|
|
return f"Renamed: {target.name} → {new_name}"
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Could not rename: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def read_file(path: str, name: str = "", max_chars: int = 4000) -> str:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
try:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
base = _resolve_path(path)
|
|
|
|
|
|
target = (base / name) if name else base
|
|
|
|
|
|
if not _is_safe_path(target):
|
|
|
|
|
|
return f"Access denied: {target}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
if not target.exists():
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return f"File not found: {target.name}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
if not target.is_file():
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return f"Not a file: {target.name}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
content = target.read_text(encoding="utf-8", errors="ignore")
|
|
|
|
|
|
if len(content) > max_chars:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
content = content[:max_chars] + f"\n\n[Truncated — {len(content)} total chars]"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
return content
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Could not read file: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def write_file(path: str, name: str = "", content: str = "",
|
|
|
|
|
|
append: bool = False) -> str:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
try:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
base = _resolve_path(path)
|
|
|
|
|
|
target = (base / name) if name else base
|
|
|
|
|
|
if not _is_safe_path(target):
|
|
|
|
|
|
return f"Access denied: {target}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
mode = "a" if append else "w"
|
|
|
|
|
|
with open(target, mode, encoding="utf-8") as f:
|
|
|
|
|
|
f.write(content)
|
|
|
|
|
|
action = "Appended to" if append else "Written to"
|
|
|
|
|
|
return f"{action}: {target.name}"
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Could not write file: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def find_files(name: str = "", extension: str = "",
|
|
|
|
|
|
path: str = "home", max_results: int = 20) -> str:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
try:
|
|
|
|
|
|
search_path = _resolve_path(path)
|
2026-04-19 15:22:49 +03:00
|
|
|
|
if not _is_safe_path(search_path):
|
|
|
|
|
|
return f"Access denied: {search_path}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
if not search_path.exists():
|
|
|
|
|
|
return f"Search path not found: {path}"
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
results = []
|
|
|
|
|
|
dir_count = 0
|
|
|
|
|
|
max_dirs = 500 # performans + güvenlik limiti
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
for item in search_path.rglob("*"):
|
|
|
|
|
|
if item.is_dir():
|
|
|
|
|
|
dir_count += 1
|
|
|
|
|
|
if dir_count > max_dirs:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
break
|
2026-04-19 15:22:49 +03:00
|
|
|
|
continue
|
|
|
|
|
|
if not item.is_file():
|
|
|
|
|
|
continue
|
|
|
|
|
|
if extension and item.suffix.lower() != extension.lower():
|
|
|
|
|
|
continue
|
|
|
|
|
|
if name and name.lower() not in item.name.lower():
|
|
|
|
|
|
continue
|
|
|
|
|
|
size = _format_size(item.stat().st_size)
|
|
|
|
|
|
results.append(f"📄 {item.name} ({size}) — {item.parent}")
|
|
|
|
|
|
if len(results) >= max_results:
|
|
|
|
|
|
break
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
if not results:
|
|
|
|
|
|
query = name or extension or "files"
|
|
|
|
|
|
return f"No {query} found in {search_path.name}/"
|
|
|
|
|
|
|
|
|
|
|
|
return f"Found {len(results)} file(s):\n" + "\n".join(results)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Search error: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def get_largest_files(path: str = "downloads", count: int = 10) -> str:
|
|
|
|
|
|
count = min(count, 50) # maksimum 50
|
2026-03-02 04:30:10 +03:00
|
|
|
|
try:
|
|
|
|
|
|
search_path = _resolve_path(path)
|
2026-04-19 15:22:49 +03:00
|
|
|
|
if not _is_safe_path(search_path):
|
|
|
|
|
|
return f"Access denied: {search_path}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
if not search_path.exists():
|
|
|
|
|
|
return f"Path not found: {path}"
|
|
|
|
|
|
|
|
|
|
|
|
files = []
|
|
|
|
|
|
for item in search_path.rglob("*"):
|
|
|
|
|
|
if item.is_file():
|
|
|
|
|
|
try:
|
|
|
|
|
|
files.append((item.stat().st_size, item))
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
files.sort(reverse=True)
|
|
|
|
|
|
top = files[:count]
|
|
|
|
|
|
|
|
|
|
|
|
if not top:
|
|
|
|
|
|
return "No files found."
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
lines = [f"Top {len(top)} largest files in {search_path.name}/:"]
|
2026-03-02 04:30:10 +03:00
|
|
|
|
for size, f in top:
|
|
|
|
|
|
lines.append(f" {_format_size(size):>10} {f.name} ({f.parent})")
|
|
|
|
|
|
|
|
|
|
|
|
return "\n".join(lines)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Error: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_disk_usage(path: str = "home") -> str:
|
|
|
|
|
|
try:
|
|
|
|
|
|
target = _resolve_path(path)
|
|
|
|
|
|
usage = shutil.disk_usage(target)
|
|
|
|
|
|
pct = usage.used / usage.total * 100
|
|
|
|
|
|
return (
|
2026-04-19 15:22:49 +03:00
|
|
|
|
f"Disk usage ({target}):\n"
|
|
|
|
|
|
f" Total : {_format_size(usage.total)}\n"
|
|
|
|
|
|
f" Used : {_format_size(usage.used)} ({pct:.1f}%)\n"
|
|
|
|
|
|
f" Free : {_format_size(usage.free)}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Could not get disk usage: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def organize_desktop() -> str:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
type_map = {
|
|
|
|
|
|
"Images": {".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg", ".ico", ".heic"},
|
|
|
|
|
|
"Documents": {".pdf", ".doc", ".docx", ".txt", ".xls", ".xlsx",
|
|
|
|
|
|
".ppt", ".pptx", ".csv", ".odt", ".ods", ".odp"},
|
|
|
|
|
|
"Videos": {".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv", ".webm", ".m4v"},
|
|
|
|
|
|
"Music": {".mp3", ".wav", ".flac", ".aac", ".ogg", ".wma", ".m4a"},
|
|
|
|
|
|
"Archives": {".zip", ".rar", ".7z", ".tar", ".gz", ".bz2", ".xz"},
|
|
|
|
|
|
"Code": {".py", ".js", ".ts", ".html", ".css", ".json", ".xml",
|
|
|
|
|
|
".cpp", ".java", ".cs", ".go", ".rs", ".sh"},
|
|
|
|
|
|
}
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
desktop = _get_desktop()
|
|
|
|
|
|
moved, skipped = [], []
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
try:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
for item in desktop.iterdir():
|
2026-04-19 15:22:49 +03:00
|
|
|
|
# Klasörlere, gizli dosyalara ve organize klasörlerine dokunma
|
2026-03-02 04:30:10 +03:00
|
|
|
|
if item.is_dir() or item.name.startswith("."):
|
|
|
|
|
|
continue
|
2026-04-19 15:22:49 +03:00
|
|
|
|
if item.name in {k for k in type_map}:
|
|
|
|
|
|
continue
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
ext = item.suffix.lower()
|
2026-04-19 15:22:49 +03:00
|
|
|
|
target_dir = desktop / "Others"
|
|
|
|
|
|
for folder, exts in type_map.items():
|
|
|
|
|
|
if ext in exts:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
target_dir = desktop / folder
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
target_dir.mkdir(exist_ok=True)
|
|
|
|
|
|
new_path = target_dir / item.name
|
|
|
|
|
|
|
|
|
|
|
|
if new_path.exists():
|
|
|
|
|
|
skipped.append(item.name)
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
shutil.move(str(item), str(new_path))
|
|
|
|
|
|
moved.append(f"{item.name} → {target_dir.name}/")
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
result = f"Desktop organized: {len(moved)} files moved."
|
2026-03-02 04:30:10 +03:00
|
|
|
|
if moved:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
preview = moved[:8]
|
|
|
|
|
|
result += "\n" + "\n".join(preview)
|
|
|
|
|
|
if len(moved) > 8:
|
|
|
|
|
|
result += f"\n... and {len(moved) - 8} more."
|
2026-03-02 04:30:10 +03:00
|
|
|
|
if skipped:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
result += f"\n{len(skipped)} file(s) skipped (name conflict)."
|
2026-03-02 04:30:10 +03:00
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Could not organize desktop: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
def get_file_info(path: str, name: str = "") -> str:
|
2026-03-02 04:30:10 +03:00
|
|
|
|
try:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
base = _resolve_path(path)
|
|
|
|
|
|
target = (base / name) if name else base
|
|
|
|
|
|
if not _is_safe_path(target):
|
|
|
|
|
|
return f"Access denied: {target}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
if not target.exists():
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return f"Not found: {target.name}"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
stat = target.stat()
|
|
|
|
|
|
info = {
|
2026-04-19 15:22:49 +03:00
|
|
|
|
"Name": target.name,
|
|
|
|
|
|
"Type": "Folder" if target.is_dir() else "File",
|
|
|
|
|
|
"Size": _format_size(stat.st_size),
|
|
|
|
|
|
"Location": str(target.parent),
|
|
|
|
|
|
"Created": datetime.fromtimestamp(stat.st_ctime).strftime("%Y-%m-%d %H:%M"),
|
|
|
|
|
|
"Modified": datetime.fromtimestamp(stat.st_mtime).strftime("%Y-%m-%d %H:%M"),
|
|
|
|
|
|
"Extension": target.suffix or "—",
|
2026-03-02 04:30:10 +03:00
|
|
|
|
}
|
|
|
|
|
|
return "\n".join(f" {k}: {v}" for k, v in info.items())
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return f"Could not get file info: {e}"
|
|
|
|
|
|
|
|
|
|
|
|
def file_controller(
|
2026-04-19 15:22:49 +03:00
|
|
|
|
parameters: dict = None,
|
2026-03-02 04:30:10 +03:00
|
|
|
|
response=None,
|
|
|
|
|
|
player=None,
|
2026-04-19 15:22:49 +03:00
|
|
|
|
session_memory=None,
|
2026-03-02 04:30:10 +03:00
|
|
|
|
) -> str:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
params = parameters or {}
|
|
|
|
|
|
action = params.get("action", "").lower().strip()
|
|
|
|
|
|
path = params.get("path", "desktop")
|
|
|
|
|
|
name = params.get("name", "")
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
2026-04-19 15:22:49 +03:00
|
|
|
|
if player:
|
|
|
|
|
|
player.write_log(f"[file] {action} {name or path}")
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
if action == "list":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return list_files(path)
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
elif action == "create_file":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return create_file(path, name=name, content=params.get("content", ""))
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
elif action == "create_folder":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return create_folder(path, name=name)
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
elif action == "delete":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return delete_file(path, name=name)
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
elif action == "move":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return move_file(path, name=name, destination=params.get("destination", ""))
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
elif action == "copy":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return copy_file(path, name=name, destination=params.get("destination", ""))
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
elif action == "rename":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return rename_file(path, name=name, new_name=params.get("new_name", ""))
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
elif action == "read":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return read_file(path, name=name)
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
elif action == "write":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return write_file(
|
|
|
|
|
|
path, name=name,
|
|
|
|
|
|
content=params.get("content", ""),
|
|
|
|
|
|
append=params.get("append", False)
|
2026-03-02 04:30:10 +03:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
elif action == "find":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return find_files(
|
|
|
|
|
|
name=name or params.get("name", ""),
|
|
|
|
|
|
extension=params.get("extension", ""),
|
2026-03-02 04:30:10 +03:00
|
|
|
|
path=path,
|
2026-04-19 15:22:49 +03:00
|
|
|
|
max_results=min(int(params.get("max_results", 20)), 50),
|
2026-03-02 04:30:10 +03:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
elif action == "largest":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return get_largest_files(
|
2026-03-02 04:30:10 +03:00
|
|
|
|
path=path,
|
2026-04-19 15:22:49 +03:00
|
|
|
|
count=int(params.get("count", 10)),
|
2026-03-02 04:30:10 +03:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
elif action == "disk_usage":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return get_disk_usage(path)
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
elif action == "organize_desktop":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return organize_desktop()
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
elif action == "info":
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return get_file_info(path, name=name)
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
else:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return f"Unknown action: '{action}'"
|
2026-03-02 04:30:10 +03:00
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
2026-04-19 15:22:49 +03:00
|
|
|
|
return f"File controller error ({action}): {e}"
|