Files
GIA/core/tasks/chat_defaults.py

125 lines
4.1 KiB
Python

from __future__ import annotations
import re
from core.models import ChatTaskSource, TaskProject
from core.tasks.codex_support import channel_variants
SAFE_TASK_FLAGS_DEFAULTS = {
"derive_enabled": True,
"match_mode": "strict",
"require_prefix": True,
"allowed_prefixes": ["task:", "todo:"],
"completion_enabled": True,
"ai_title_enabled": True,
"announce_task_id": False,
"min_chars": 3,
}
def normalize_channel_identifier(service: str, identifier: str) -> str:
service_key = str(service or "").strip().lower()
value = str(identifier or "").strip()
if not value:
return ""
if service_key == "whatsapp":
bare = value.split("@", 1)[0].strip()
if not bare:
return value
if value.endswith("@s.whatsapp.net"):
return f"{bare}@s.whatsapp.net"
return f"{bare}@g.us"
return value
def resolve_message_scope(message) -> tuple[str, str]:
source_service = str(getattr(message, "source_service", "") or "").strip().lower()
source_channel = str(getattr(message, "source_chat_id", "") or "").strip()
if source_service != "web":
return source_service, source_channel
identifier = getattr(getattr(message, "session", None), "identifier", None)
fallback_service = str(getattr(identifier, "service", "") or "").strip().lower()
fallback_identifier = str(getattr(identifier, "identifier", "") or "").strip()
if fallback_service and fallback_identifier and fallback_service != "web":
return fallback_service, fallback_identifier
return source_service, source_channel
def _project_name_candidate(service: str, channel_identifier: str, message=None) -> str:
person_name = ""
if message is not None:
identifier = getattr(getattr(message, "session", None), "identifier", None)
person = getattr(identifier, "person", None)
person_name = str(getattr(person, "name", "") or "").strip()
if person_name:
return person_name[:255]
raw = str(channel_identifier or "").strip()
if str(service or "").strip().lower() == "whatsapp":
raw = raw.split("@", 1)[0].strip()
cleaned = re.sub(r"\s+", " ", raw).strip()
if not cleaned:
cleaned = "Chat"
return f"Chat: {cleaned}"[:255]
def _ensure_unique_project_name(user, base_name: str) -> str:
base = str(base_name or "").strip() or "Chat"
if not TaskProject.objects.filter(user=user, name=base).exists():
return base
idx = 2
while idx < 10000:
candidate = f"{base} ({idx})"[:255]
if not TaskProject.objects.filter(user=user, name=candidate).exists():
return candidate
idx += 1
return f"{base} ({str(user.id)[:8]})"[:255]
def ensure_default_source_for_chat(
*,
user,
service: str,
channel_identifier: str,
message=None,
):
service_key = str(service or "").strip().lower()
normalized_identifier = normalize_channel_identifier(
service_key, channel_identifier
)
variants = channel_variants(service_key, normalized_identifier)
if not service_key or not variants:
return None
existing = (
ChatTaskSource.objects.filter(
user=user,
service=service_key,
channel_identifier__in=variants,
)
.select_related("project", "epic")
.order_by("-enabled", "-updated_at", "-created_at")
.first()
)
if existing is not None:
if not existing.enabled:
existing.enabled = True
existing.save(update_fields=["enabled", "updated_at"])
return existing
project_name = _ensure_unique_project_name(
user,
_project_name_candidate(service_key, normalized_identifier, message=message),
)
project = TaskProject.objects.create(
user=user,
name=project_name,
settings=dict(SAFE_TASK_FLAGS_DEFAULTS),
)
return ChatTaskSource.objects.create(
user=user,
service=service_key,
channel_identifier=normalized_identifier,
project=project,
epic=None,
enabled=True,
settings=dict(SAFE_TASK_FLAGS_DEFAULTS),
)