release: v2.8.21

This commit is contained in:
Christian 2026-09-26 09:51:42 +02:00
parent 9b662c3f1a
commit 4058451565
5 changed files with 31 additions and 15 deletions

View File

@ -1 +1 @@
2.8.20 2.8.21

View File

@ -732,7 +732,8 @@ async def mission_telefoni_ringing(event: MissionCallEvent, request: Request, to
timestamp = event.timestamp or datetime.utcnow() timestamp = event.timestamp or datetime.utcnow()
context = MissionService.resolve_contact_context(event.caller_number) context = MissionService.resolve_contact_context(event.caller_number)
queue_name = _display_destination(event) queue_name = (event.queue_name or "Ukendt kø").strip()
recipient_name = _display_destination(event)
execute_query( execute_query(
""" """
@ -753,7 +754,13 @@ async def mission_telefoni_ringing(event: MissionCallEvent, request: Request, to
answered_at = NULL, answered_at = NULL,
started_at = LEAST(mission_call_state.started_at, EXCLUDED.started_at), started_at = LEAST(mission_call_state.started_at, EXCLUDED.started_at),
updated_at = NOW(), updated_at = NOW(),
last_payload = EXCLUDED.last_payload last_payload = jsonb_set(
EXCLUDED.last_payload,
'{ringing_phones}',
COALESCE(mission_call_state.last_payload->'ringing_phones', '[]'::jsonb)
|| COALESCE(EXCLUDED.last_payload->'ringing_phones', '[]'::jsonb),
true
)
""", """,
( (
event.call_id, event.call_id,
@ -763,7 +770,7 @@ async def mission_telefoni_ringing(event: MissionCallEvent, request: Request, to
context.get("company_name"), context.get("company_name"),
context.get("customer_tag"), context.get("customer_tag"),
timestamp, timestamp,
json.dumps(event.model_dump(mode="json")), json.dumps({**event.model_dump(mode="json"), "ringing_phones": [recipient_name]}),
), ),
) )
@ -786,6 +793,7 @@ async def mission_telefoni_ringing(event: MissionCallEvent, request: Request, to
"queue_name": queue_name, "queue_name": queue_name,
"caller_number": event.caller_number, "caller_number": event.caller_number,
"recipient_extension": event.recipient_extension, "recipient_extension": event.recipient_extension,
"ringing_phones": [recipient_name],
**context, **context,
"timestamp": timestamp, "timestamp": timestamp,
} }

View File

@ -26,12 +26,10 @@ class MissionService:
@staticmethod @staticmethod
def get_ring_timeout_seconds() -> int: def get_ring_timeout_seconds() -> int:
raw = MissionService.get_setting_value("mission_call_ring_timeout_seconds", "180") or "180" # A Yealink "Incoming call" event has no matching event when the
try: # caller abandons the call. Keep it briefly, then remove it from the
value = int(raw) # live boards so stale ringing phones cannot remain visible.
except (TypeError, ValueError): return 30
value = 180
return max(30, min(value, 3600))
@staticmethod @staticmethod
def expire_stale_ringing_calls() -> None: def expire_stale_ringing_calls() -> None:
@ -216,7 +214,8 @@ class MissionService:
MissionService.expire_stale_ringing_calls() MissionService.expire_stale_ringing_calls()
rows = execute_query( rows = execute_query(
""" """
SELECT call_id, queue_name, caller_number, contact_name, company_name, customer_tag, state, started_at, answered_at, ended_at, updated_at SELECT call_id, queue_name, caller_number, contact_name, company_name, customer_tag, state, started_at, answered_at, ended_at, updated_at,
COALESCE(last_payload->'ringing_phones', jsonb_build_array(queue_name)) AS ringing_phones
FROM mission_call_state FROM mission_call_state
WHERE state = 'ringing' WHERE state = 'ringing'
ORDER BY started_at DESC ORDER BY started_at DESC

View File

@ -2956,8 +2956,10 @@
} }
const call = state.activeCalls[0]; const call = state.activeCalls[0];
const phones = [...new Set((Array.isArray(call.ringing_phones) ? call.ringing_phones : [call.queue_name]).filter(Boolean))];
title.textContent = `${call.queue_name || 'Ukendt kø'} - ${call.caller_number || 'Ukendt nummer'}`; title.textContent = `${call.queue_name || 'Ukendt kø'} - ${call.caller_number || 'Ukendt nummer'}`;
const parts = []; const parts = [];
if (phones.length) parts.push(`Ringer hos: ${phones.join(' · ')}`);
if (call.contact_name) parts.push(call.contact_name); if (call.contact_name) parts.push(call.contact_name);
if (call.company_name) parts.push(call.company_name); if (call.company_name) parts.push(call.company_name);
parts.push(`Start: ${formatDate(call.started_at)}`); parts.push(`Start: ${formatDate(call.started_at)}`);
@ -2973,16 +2975,20 @@
return; return;
} }
list.innerHTML = state.activeCalls.map((call) => ` list.innerHTML = state.activeCalls.map((call) => {
const phones = [...new Set((Array.isArray(call.ringing_phones) ? call.ringing_phones : [call.queue_name]).filter(Boolean))];
return `
<div class="mc-feed-item"> <div class="mc-feed-item">
<div class="mc-feed-title">${escapeHtml(call.queue_name || 'Ukendt kø')} - ${escapeHtml(call.caller_number || '-')}</div> <div class="mc-feed-title">${escapeHtml(call.queue_name || 'Ukendt kø')} - ${escapeHtml(call.caller_number || '-')}</div>
<div class="mc-feed-meta"> <div class="mc-feed-meta">
${phones.length ? `Ringer hos: ${escapeHtml(phones.join(' · '))} • ` : ''}
${escapeHtml(call.contact_name || 'Ukendt kontakt')} ${escapeHtml(call.contact_name || 'Ukendt kontakt')}
${call.company_name ? ` • ${escapeHtml(call.company_name)}` : ''} ${call.company_name ? ` • ${escapeHtml(call.company_name)}` : ''}
${call.started_at ? ` • ${escapeHtml(formatDate(call.started_at))}` : ''} ${call.started_at ? ` • ${escapeHtml(formatDate(call.started_at))}` : ''}
</div> </div>
</div> </div>
`).join(''); `;
}).join('');
} }
function renderCallHistory() { function renderCallHistory() {

View File

@ -338,13 +338,16 @@ function renderLiveCalls(calls) {
list.innerHTML = calls.map((call) => { list.innerHTML = calls.map((call) => {
const caller = escapeHtml(normalizeDisplayNumber(call.caller_number) || call.caller_number || 'Ukendt nummer'); const caller = escapeHtml(normalizeDisplayNumber(call.caller_number) || call.caller_number || 'Ukendt nummer');
const recipient = escapeHtml(call.queue_name || 'Ukendt telefon'); const phones = [...new Set((Array.isArray(call.ringing_phones) ? call.ringing_phones : [call.queue_name]).filter(Boolean))];
const recipient = escapeHtml(phones.join(' · ') || 'Ukendt telefon');
const queue = escapeHtml(call.queue_name || 'Ukendt kø');
const contactParts = [call.contact_name, call.company_name].filter(Boolean).map(escapeHtml); const contactParts = [call.contact_name, call.company_name].filter(Boolean).map(escapeHtml);
const contact = contactParts.length ? ` <span class="text-muted">· ${contactParts.join(' · ')}</span>` : ''; const contact = contactParts.length ? ` <span class="text-muted">· ${contactParts.join(' · ')}</span>` : '';
return `<div class="d-flex align-items-center flex-wrap gap-2 py-1"> return `<div class="d-flex align-items-center flex-wrap gap-2 py-1">
<span class="badge text-bg-danger">RINGER</span> <span class="badge text-bg-danger">RINGER</span>
<strong>${recipient}</strong> <strong>${recipient}</strong>
<span>fra ${caller}</span>${contact} <span>fra ${caller}</span>
<span class="badge text-bg-light border">${queue}</span>${contact}
</div>`; </div>`;
}).join(''); }).join('');
} }