PMFlow/backend/app/services/sstc/feishu_routing.py

110 lines
3.8 KiB
Python

from __future__ import annotations
from .shared import *
from .charter_seed_utils import _safe_threshold
from .signal_core import _as_dict, _signal_source_context
from .signal_filters import _replace_signal_sender_in_content, _signal_proposer_name
def _extract_feishu_routing_config(integration: Optional[Integration]) -> dict[str, Any]:
config: dict[str, Any] = {}
if integration is not None:
if isinstance(integration.config, dict):
config = integration.config
elif isinstance(integration.config, str):
try:
parsed = json.loads(integration.config)
if isinstance(parsed, dict):
config = parsed
except (TypeError, ValueError):
config = {}
cold_start_enabled = bool(config.get("cold_start_enabled"))
signal_threshold = _safe_threshold(
config.get("signal_auto_enter_threshold"),
DEFAULT_SIGNAL_AUTO_ENTER_THRESHOLD,
)
project_threshold = _safe_threshold(
config.get("project_auto_create_threshold"),
DEFAULT_PROJECT_AUTO_CREATE_THRESHOLD,
)
uncertain_threshold = min(
project_threshold,
_safe_threshold(
config.get("project_uncertain_threshold"),
DEFAULT_PROJECT_UNCERTAIN_THRESHOLD,
),
)
return {
"raw": config,
"cold_start_enabled": cold_start_enabled,
"signal_auto_enter_threshold": signal_threshold,
"project_auto_create_threshold": project_threshold,
"project_uncertain_threshold": uncertain_threshold,
}
async def _backfill_feishu_sender_names(
db: AsyncSession,
*,
tenant_id: int,
signals: list[Signal],
) -> None:
pending: dict[int, dict[str, str]] = {}
for signal in signals:
if str(signal.source or "").lower() != "feishu":
continue
source_context = _signal_source_context(signal)
open_id = str(source_context.get("sender_open_id") or "").strip()
sender = _signal_proposer_name(signal).strip()
if not open_id or not sender:
continue
if _FEISHU_PLACEHOLDER_RE.match(sender):
pending[int(signal.id)] = {
"open_id": open_id,
"current_sender": sender,
}
if not pending:
return
service = get_feishu_service(db)
await service.load_config(tenant_id=tenant_id)
if not service.app_id or not service.app_secret:
return
resolved_name_by_open_id: dict[str, str] = {}
for open_id in sorted({meta["open_id"] for meta in pending.values()})[:20]:
try:
remote_name = await service._fetch_user_name_by_open_id(open_id) # noqa: SLF001
except Exception:
remote_name = None
normalized = (remote_name or "").strip()
if not normalized or _FEISHU_PLACEHOLDER_RE.match(normalized):
continue
resolved_name_by_open_id[open_id] = normalized
if not resolved_name_by_open_id:
return
changed = False
for signal in signals:
meta = pending.get(int(signal.id))
if not meta:
continue
sender_name = resolved_name_by_open_id.get(meta["open_id"])
if not sender_name:
continue
payload = _as_dict(signal.strategic_analysis)
source_context = payload.get("source_context")
if not isinstance(source_context, dict):
source_context = {}
source_context["sender"] = sender_name
source_context["sender_open_id"] = meta["open_id"]
payload["source_context"] = source_context
signal.strategic_analysis = json.dumps(payload, ensure_ascii=False)
signal.content = _replace_signal_sender_in_content(str(signal.content or ""), sender_name)
changed = True
if changed:
await db.commit()