KI-AGENT: Anrufhistorie für Telefonie ergänzen

This commit is contained in:
2026-05-21 15:54:24 +02:00
parent ba12c46c88
commit 9e7b5bc0b9
7 changed files with 483 additions and 0 deletions

View File

@@ -1,9 +1,11 @@
const loading = ref(false)
const statusLoading = ref(false)
const websocketTesting = ref(false)
const callHistoryLoading = ref(false)
const config = ref(null)
const status = ref(null)
const websocketResult = ref(null)
const callHistory = ref([])
const lastUpdated = ref(null)
const selectedExtension = ref("1001")
const dialTarget = ref("600")
@@ -24,6 +26,9 @@ const ringtoneTimer = shallowRef(null)
const ringtoneAudioContext = shallowRef(null)
const callSignalStatus = ref("Bereit")
const originalDocumentTitle = ref("")
const activeCallRecordId = ref(null)
const activeCallDirection = ref(null)
const activeCallAnsweredAt = ref(null)
export const useTelephonySoftphone = () => {
const toast = useToast()
@@ -78,6 +83,98 @@ export const useTelephonySoftphone = () => {
].slice(0, 8)
}
const sipCallIdFromSession = (session) =>
session?.request?.callId
|| session?.request?.message?.callId
|| session?.incomingInviteRequest?.message?.callId
|| session?.outgoingInviteRequest?.message?.callId
|| null
const loadCallHistory = async () => {
callHistoryLoading.value = true
try {
callHistory.value = await $api("/api/telephony/calls?limit=20")
} catch (error) {
addSipEvent(`Anrufhistorie konnte nicht geladen werden: ${error?.message || "Unbekannter Fehler"}`)
} finally {
callHistoryLoading.value = false
}
}
const createCallRecord = async ({ direction, status: nextStatus, localExtension, remoteNumber, remoteDisplayName, session }) => {
try {
const call = await $api("/api/telephony/calls", {
method: "POST",
body: {
direction,
status: nextStatus,
localExtension,
remoteNumber,
remoteDisplayName,
sipCallId: sipCallIdFromSession(session),
startedAt: new Date().toISOString(),
},
})
activeCallRecordId.value = call.id
activeCallDirection.value = direction
activeCallAnsweredAt.value = null
await loadCallHistory()
return call
} catch (error) {
addSipEvent(`Anrufhistorie konnte nicht geschrieben werden: ${error?.message || "Unbekannter Fehler"}`)
return null
}
}
const updateCallRecord = async (id, payload) => {
if (!id) return null
try {
const call = await $api(`/api/telephony/calls/${id}`, {
method: "PATCH",
body: payload,
})
await loadCallHistory()
return call
} catch (error) {
addSipEvent(`Anrufhistorie konnte nicht aktualisiert werden: ${error?.message || "Unbekannter Fehler"}`)
return null
}
}
const markCallAnswered = async () => {
if (!activeCallRecordId.value || activeCallAnsweredAt.value) return
activeCallAnsweredAt.value = new Date()
await updateCallRecord(activeCallRecordId.value, {
status: "active",
answeredAt: activeCallAnsweredAt.value.toISOString(),
})
}
const finalizeCallRecord = async (statusOverride = null) => {
if (!activeCallRecordId.value) return
const id = activeCallRecordId.value
const direction = activeCallDirection.value
const answeredAt = activeCallAnsweredAt.value
const endedAt = new Date()
const finalStatus = statusOverride
|| (answeredAt ? "completed" : direction === "incoming" ? "missed" : "canceled")
activeCallRecordId.value = null
activeCallDirection.value = null
activeCallAnsweredAt.value = null
await updateCallRecord(id, {
status: finalStatus,
endedAt: endedAt.toISOString(),
})
}
const ensureSipModule = async () => {
if (!sipModule.value) {
sipModule.value = await import("sip.js")
@@ -199,10 +296,12 @@ export const useTelephonySoftphone = () => {
callState.value = "active"
sipStatus.value = "Im Gespräch"
incomingCall.value = null
await markCallAnswered()
await attachRemoteAudio(session)
}
if (state === SessionState.Terminated) {
await finalizeCallRecord()
stopCallSignaling()
callState.value = "terminated"
sipStatus.value = sipRegistered.value ? "Registriert" : "Nicht verbunden"
@@ -228,6 +327,8 @@ export const useTelephonySoftphone = () => {
if (!selectedAccount.value && configRes?.testAccounts?.length) {
selectedExtension.value = configRes.testAccounts[0].extension
}
await loadCallHistory()
} catch (error) {
toast.add({
title: "Telefonie-Status konnte nicht geladen werden",
@@ -346,9 +447,18 @@ export const useTelephonySoftphone = () => {
if (!uri) throw new Error("SIP-URI konnte nicht erstellt werden.")
const handleIncomingInvite = (invitation) => {
const remoteNumber = invitation.remoteIdentity?.uri?.user || null
addSipEvent(`INVITE von ${invitation.remoteIdentity?.uri?.user || "unbekannt"} empfangen`)
incomingCall.value = invitation
setupSession(invitation, "incoming")
void createCallRecord({
direction: "incoming",
status: "ringing",
localExtension: account.extension,
remoteNumber,
remoteDisplayName: invitation.remoteIdentity?.displayName || remoteNumber,
session: invitation,
})
startCallSignaling()
toast.add({
@@ -477,9 +587,18 @@ export const useTelephonySoftphone = () => {
},
})
await createCallRecord({
direction: "outgoing",
status: "dialing",
localExtension: selectedAccount.value?.extension || selectedExtension.value,
remoteNumber: dialTarget.value.trim(),
remoteDisplayName: dialTarget.value.trim(),
session: inviter,
})
setupSession(inviter, "outgoing")
await inviter.invite()
} catch (error) {
await finalizeCallRecord("failed")
activeSession.value = null
callState.value = "idle"
sipError.value = error?.message || "Anruf konnte nicht gestartet werden."
@@ -517,6 +636,7 @@ export const useTelephonySoftphone = () => {
try {
stopCallSignaling()
await incomingCall.value.reject()
await finalizeCallRecord("rejected")
} finally {
incomingCall.value = null
activeSession.value = null
@@ -541,6 +661,7 @@ export const useTelephonySoftphone = () => {
session.dispose()
}
} finally {
await finalizeCallRecord()
stopCallSignaling()
activeSession.value = null
incomingCall.value = null
@@ -554,9 +675,11 @@ export const useTelephonySoftphone = () => {
loading,
statusLoading,
websocketTesting,
callHistoryLoading,
config,
status,
websocketResult,
callHistory,
lastUpdated,
selectedExtension,
dialTarget,
@@ -580,6 +703,7 @@ export const useTelephonySoftphone = () => {
statusIcon,
websocketColor,
loadTelephony,
loadCallHistory,
refreshStatus,
testWebSocket,
registerSip,