deer-flow/backend/app/channels/inbound_files.py
Li-john1021 32d7dd5fe8
fix(discord): accept inbound attachments and file-only messages (#6857)
* fix(discord): accept inbound attachments and file-only messages

Empty content no longer drops messages that have attachments. Capture
CDN descriptors on inbound, download in receive_file via Bot auth, and
hand bytes through INBOUND_FILE_CONTENT_KEY like Slack/Telegram.

Closes #6856

* fix(discord): gate bot auth and share inbound file helpers

Address review on #6857: attach Bot Authorization only for Discord CDN
hosts, report Content-Encoding refusals as download failed, and hoist
filename/size helpers into app.channels.inbound_files (Telegram delegates
to the same module).

* fix(discord): sort telegram imports for ruff I001

CI lint-backend failed on unsorted imports in telegram.py after the
shared inbound_files helper was introduced.

* style: ruff-format discord inbound attachments test

* fix(discord): show the enforced attachment download limit

---------

Co-authored-by: Li-john1021 <Li-john1021@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-14 23:01:02 +08:00

42 lines
1.4 KiB
Python

"""Shared helpers for inbound channel file descriptors.
Used by Telegram/Discord (and any future channel) so filename sanitization and
size coercion cannot drift between adapters that feed the same upload pipeline.
"""
from __future__ import annotations
from typing import Any
from deerflow.uploads.manager import is_upload_staging_file, normalize_filename
def safe_inbound_filename(value: Any, fallback: str) -> str:
"""Normalize a user-supplied filename or return *fallback* when unsafe."""
if not isinstance(value, str):
return fallback
try:
candidate = normalize_filename(value.strip())
except (UnicodeError, ValueError):
return fallback
if is_upload_staging_file(candidate) or any(ord(char) < 32 for char in candidate):
return fallback
return candidate
def coerce_file_size(value: Any) -> int | None:
"""Coerce a payload size field to a non-negative int, or ``None``.
Whitelists bool/int/float/str shapes so accidental truthy values (and
truncated floats) never become sizes.
"""
if isinstance(value, bool):
return None
if isinstance(value, int):
return value if value >= 0 else None
if isinstance(value, float) and value.is_integer() and value >= 0:
return int(value)
if isinstance(value, str) and value.strip().isdigit():
return int(value.strip())
return None