Fix Signal messages and replies
This commit is contained in:
@@ -7,9 +7,11 @@ from django.db import transaction
|
||||
from django.db.models import Avg, Count, Q, Sum
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.views import View
|
||||
|
||||
from core.commands.policies import BP_VARIANT_KEYS, BP_VARIANT_META, ensure_variant_policies_for_profile
|
||||
from core.models import (
|
||||
AIRunLog,
|
||||
BusinessPlanDocument,
|
||||
@@ -17,12 +19,29 @@ from core.models import (
|
||||
CommandAction,
|
||||
CommandChannelBinding,
|
||||
CommandProfile,
|
||||
CommandVariantPolicy,
|
||||
TranslationBridge,
|
||||
TranslationEventLog,
|
||||
)
|
||||
from core.translation.engine import parse_quick_mode_title
|
||||
|
||||
|
||||
def _channel_variants(service: str, identifier: str) -> list[str]:
|
||||
value = str(identifier or "").strip()
|
||||
if not value:
|
||||
return []
|
||||
variants = [value]
|
||||
svc = str(service or "").strip().lower()
|
||||
if svc == "whatsapp":
|
||||
bare = value.split("@", 1)[0].strip()
|
||||
if bare and bare not in variants:
|
||||
variants.append(bare)
|
||||
group = f"{bare}@g.us" if bare else ""
|
||||
if group and group not in variants:
|
||||
variants.append(group)
|
||||
return variants
|
||||
|
||||
|
||||
class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
template_name = "pages/command-routing.html"
|
||||
|
||||
@@ -35,12 +54,71 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
row.save(update_fields=["position", "updated_at"])
|
||||
return rows
|
||||
|
||||
@staticmethod
|
||||
def _redirect_with_scope(request):
|
||||
service = str(request.GET.get("service") or request.POST.get("service") or "").strip()
|
||||
identifier = str(
|
||||
request.GET.get("identifier") or request.POST.get("identifier") or ""
|
||||
).strip()
|
||||
if service and identifier:
|
||||
return redirect(
|
||||
f"{reverse('command_routing')}?service={service}&identifier={identifier}"
|
||||
)
|
||||
return redirect("command_routing")
|
||||
|
||||
def _context(self, request):
|
||||
profiles = (
|
||||
profiles_qs = (
|
||||
CommandProfile.objects.filter(user=request.user)
|
||||
.prefetch_related("channel_bindings", "actions")
|
||||
.prefetch_related("channel_bindings", "actions", "variant_policies")
|
||||
.order_by("slug")
|
||||
)
|
||||
scope_service = str(request.GET.get("service") or "").strip().lower()
|
||||
scope_identifier = str(request.GET.get("identifier") or "").strip()
|
||||
scope_variants = _channel_variants(scope_service, scope_identifier)
|
||||
profiles = list(profiles_qs)
|
||||
preview_profile_id = str(request.GET.get("preview_profile_id") or "").strip()
|
||||
for profile in profiles:
|
||||
policies = ensure_variant_policies_for_profile(profile)
|
||||
if str(profile.slug or "").strip() == "bp":
|
||||
keys = BP_VARIANT_KEYS
|
||||
else:
|
||||
keys = ("default",)
|
||||
profile.variant_rows = []
|
||||
for key in keys:
|
||||
row = policies.get(key)
|
||||
if row is None:
|
||||
continue
|
||||
meta = BP_VARIANT_META.get(key, {})
|
||||
profile.variant_rows.append(
|
||||
{
|
||||
"variant_key": key,
|
||||
"variant_label": str(meta.get("name") or key),
|
||||
"trigger_token": str(meta.get("trigger_token") or profile.trigger_token or ""),
|
||||
"template_supported": bool(meta.get("template_supported")),
|
||||
"warn_verbatim_plan": bool(
|
||||
key in {"bp", "bp_set_range"}
|
||||
and str(getattr(row, "generation_mode", "") or "") == "verbatim"
|
||||
and bool(getattr(row, "send_plan_to_egress", False))
|
||||
),
|
||||
"row": row,
|
||||
}
|
||||
)
|
||||
bindings = list(profile.channel_bindings.all())
|
||||
if scope_service and scope_variants:
|
||||
profile.visible_bindings = [
|
||||
row
|
||||
for row in bindings
|
||||
if str(row.service or "").strip().lower() == scope_service
|
||||
and str(row.channel_identifier or "").strip() in scope_variants
|
||||
]
|
||||
else:
|
||||
profile.visible_bindings = bindings
|
||||
profile.enabled_egress_bindings = [
|
||||
row
|
||||
for row in bindings
|
||||
if str(row.direction or "").strip() == "egress" and bool(row.enabled)
|
||||
]
|
||||
profile.preview_mode = preview_profile_id and str(profile.id) == preview_profile_id
|
||||
documents = BusinessPlanDocument.objects.filter(user=request.user).order_by(
|
||||
"-updated_at"
|
||||
)[:30]
|
||||
@@ -50,6 +128,11 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
"channel_services": ("web", "xmpp", "signal", "whatsapp"),
|
||||
"directions": ("ingress", "egress", "scratchpad_mirror"),
|
||||
"action_types": ("extract_bp", "post_result", "save_document"),
|
||||
"command_choices": (("bp", "Business Plan (bp)"),),
|
||||
"scope_service": scope_service,
|
||||
"scope_identifier": scope_identifier,
|
||||
"scope_variants": scope_variants,
|
||||
"preview_profile_id": preview_profile_id,
|
||||
}
|
||||
|
||||
def get(self, request):
|
||||
@@ -59,7 +142,12 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
action = str(request.POST.get("action") or "").strip()
|
||||
|
||||
if action == "profile_create":
|
||||
slug = str(request.POST.get("slug") or "bp").strip().lower() or "bp"
|
||||
slug = (
|
||||
str(request.POST.get("command_slug") or request.POST.get("slug") or "bp")
|
||||
.strip()
|
||||
.lower()
|
||||
or "bp"
|
||||
)
|
||||
profile, _ = CommandProfile.objects.get_or_create(
|
||||
user=request.user,
|
||||
slug=slug,
|
||||
@@ -74,6 +162,11 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
"template_text": str(request.POST.get("template_text") or ""),
|
||||
},
|
||||
)
|
||||
profile.name = str(request.POST.get("name") or profile.name).strip() or profile.name
|
||||
if slug == "bp":
|
||||
profile.trigger_token = "#bp#"
|
||||
profile.template_text = str(request.POST.get("template_text") or profile.template_text or "")
|
||||
profile.save(update_fields=["name", "trigger_token", "template_text", "updated_at"])
|
||||
CommandAction.objects.get_or_create(
|
||||
profile=profile,
|
||||
action_type="extract_bp",
|
||||
@@ -89,7 +182,8 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
action_type="post_result",
|
||||
defaults={"enabled": True, "position": 2},
|
||||
)
|
||||
return redirect("command_routing")
|
||||
ensure_variant_policies_for_profile(profile)
|
||||
return self._redirect_with_scope(request)
|
||||
|
||||
if action == "profile_update":
|
||||
profile = get_object_or_404(
|
||||
@@ -106,12 +200,11 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
profile.reply_required = bool(request.POST.get("reply_required"))
|
||||
profile.exact_match_only = bool(request.POST.get("exact_match_only"))
|
||||
profile.template_text = str(request.POST.get("template_text") or "")
|
||||
profile.visibility_mode = (
|
||||
str(request.POST.get("visibility_mode") or "status_in_source").strip()
|
||||
or "status_in_source"
|
||||
)
|
||||
# Legacy field retained for compatibility only.
|
||||
profile.visibility_mode = profile.visibility_mode or "status_in_source"
|
||||
profile.save()
|
||||
return redirect("command_routing")
|
||||
ensure_variant_policies_for_profile(profile)
|
||||
return self._redirect_with_scope(request)
|
||||
|
||||
if action == "profile_delete":
|
||||
profile = get_object_or_404(
|
||||
@@ -120,7 +213,7 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
user=request.user,
|
||||
)
|
||||
profile.delete()
|
||||
return redirect("command_routing")
|
||||
return self._redirect_with_scope(request)
|
||||
|
||||
if action == "binding_create":
|
||||
profile = get_object_or_404(
|
||||
@@ -137,7 +230,7 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
).strip(),
|
||||
enabled=bool(request.POST.get("enabled") or "1"),
|
||||
)
|
||||
return redirect("command_routing")
|
||||
return self._redirect_with_scope(request)
|
||||
|
||||
if action == "binding_delete":
|
||||
binding = get_object_or_404(
|
||||
@@ -146,7 +239,7 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
profile__user=request.user,
|
||||
)
|
||||
binding.delete()
|
||||
return redirect("command_routing")
|
||||
return self._redirect_with_scope(request)
|
||||
|
||||
if action == "action_update":
|
||||
row = get_object_or_404(
|
||||
@@ -160,7 +253,7 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
row.save(update_fields=["enabled", "position", "updated_at"])
|
||||
else:
|
||||
row.save(update_fields=["enabled", "updated_at"])
|
||||
return redirect("command_routing")
|
||||
return self._redirect_with_scope(request)
|
||||
|
||||
if action == "action_move":
|
||||
row = get_object_or_404(
|
||||
@@ -170,26 +263,74 @@ class CommandRoutingSettings(LoginRequiredMixin, View):
|
||||
)
|
||||
direction = str(request.POST.get("direction") or "").strip().lower()
|
||||
if direction not in {"up", "down"}:
|
||||
return redirect("command_routing")
|
||||
return self._redirect_with_scope(request)
|
||||
with transaction.atomic():
|
||||
ordered = self._normalize_action_positions(row.profile)
|
||||
action_ids = [entry.id for entry in ordered]
|
||||
try:
|
||||
idx = action_ids.index(row.id)
|
||||
except ValueError:
|
||||
return redirect("command_routing")
|
||||
return self._redirect_with_scope(request)
|
||||
target_idx = idx - 1 if direction == "up" else idx + 1
|
||||
if target_idx < 0 or target_idx >= len(ordered):
|
||||
return redirect("command_routing")
|
||||
return self._redirect_with_scope(request)
|
||||
other = ordered[target_idx]
|
||||
current_pos = ordered[idx].position
|
||||
ordered[idx].position = other.position
|
||||
other.position = current_pos
|
||||
ordered[idx].save(update_fields=["position", "updated_at"])
|
||||
other.save(update_fields=["position", "updated_at"])
|
||||
return redirect("command_routing")
|
||||
return self._redirect_with_scope(request)
|
||||
|
||||
return redirect("command_routing")
|
||||
if action == "variant_policy_update":
|
||||
profile = get_object_or_404(
|
||||
CommandProfile,
|
||||
id=request.POST.get("profile_id"),
|
||||
user=request.user,
|
||||
)
|
||||
variant_key = str(request.POST.get("variant_key") or "").strip()
|
||||
policy = get_object_or_404(
|
||||
CommandVariantPolicy,
|
||||
profile=profile,
|
||||
variant_key=variant_key,
|
||||
)
|
||||
policy.enabled = bool(request.POST.get("enabled"))
|
||||
mode = str(request.POST.get("generation_mode") or "verbatim").strip().lower()
|
||||
policy.generation_mode = mode if mode in {"ai", "verbatim"} else "verbatim"
|
||||
policy.send_plan_to_egress = bool(request.POST.get("send_plan_to_egress"))
|
||||
policy.send_status_to_source = bool(request.POST.get("send_status_to_source"))
|
||||
policy.send_status_to_egress = bool(request.POST.get("send_status_to_egress"))
|
||||
policy.store_document = bool(request.POST.get("store_document"))
|
||||
policy.save()
|
||||
return self._redirect_with_scope(request)
|
||||
|
||||
if action == "variant_policy_reset_defaults":
|
||||
profile = get_object_or_404(
|
||||
CommandProfile,
|
||||
id=request.POST.get("profile_id"),
|
||||
user=request.user,
|
||||
)
|
||||
profile.variant_policies.all().delete()
|
||||
ensure_variant_policies_for_profile(profile)
|
||||
return self._redirect_with_scope(request)
|
||||
|
||||
if action == "variant_preview":
|
||||
profile = get_object_or_404(
|
||||
CommandProfile,
|
||||
id=request.POST.get("profile_id"),
|
||||
user=request.user,
|
||||
)
|
||||
ensure_variant_policies_for_profile(profile)
|
||||
service = str(request.GET.get("service") or request.POST.get("service") or "").strip()
|
||||
identifier = str(
|
||||
request.GET.get("identifier") or request.POST.get("identifier") or ""
|
||||
).strip()
|
||||
query = f"?preview_profile_id={profile.id}"
|
||||
if service and identifier:
|
||||
query += f"&service={service}&identifier={identifier}"
|
||||
return redirect(f"{reverse('command_routing')}{query}")
|
||||
|
||||
return self._redirect_with_scope(request)
|
||||
|
||||
|
||||
class TranslationSettings(LoginRequiredMixin, View):
|
||||
|
||||
Reference in New Issue
Block a user