feat: implement Call Center queues (mod_callcenter)
- Investigated the real callcenter_config command surface via
'help callcenter_config' on the running FreeSWITCH before writing any
code: queues only have load/unload/reload (static XML + reload, no
'queue add' exists), while agents and tiers are fully dynamic via ESL
commands (agent add, tier add) -- no file involved. This shapes the next
phase (Agents/Tiers) differently from this one.
- queues table (tenant-scoped, RLS): strategy, moh/announce, wait times,
tier rules, discard/abandoned handling, skip-agents-with-external-calls,
recording_enabled
- packages/telephony: buildQueueXml()
- infrastructure/freeswitch: our own callcenter.conf.xml override (empties
the vanilla static agents/tiers -- those become fully dynamic in the next
phase) that includes callcenter_queues.conf.d/*.xml via X-PRE-PROCESS,
same pattern as the Sofia gateway directory
- apps/api/src/queues: CRUD (POST/GET/GET:id/DELETE) using the queues.view/
.manage permissions already in the seed
- b2bcall-fs-config (queue-sync.ts): one XML file per queue on a shared
volume, synced via Redis pub/sub (b2bcall:queues:sync) on create/delete
and once at boot -- same shape as trunk-sync.ts
- confirmed manually against the real FreeSWITCH, before coding the sync
logic: 'queue load <name>' fails ('Invalid Queue not found!') for a file
added after boot -- needs 'reloadxml' first to repopulate the in-memory
XML tree from disk; after that, 'queue reload <name>' alone handles both
create and update, no need to distinguish load vs reload
- verified end-to-end: created a queue (ROUND_ROBIN, maxWaitTime=120,
discardAbandonedAfter=90), 'callcenter_config queue list' showed the
correct values on the FreeSWITCH side; deleted it, list went back empty
docs/QUEUES.md
This commit is contained in:
@@ -38,6 +38,7 @@ model Tenant {
|
||||
trunks Trunk[]
|
||||
dialplanExtensions DialplanExtension[]
|
||||
dialplanVersions DialplanVersion[]
|
||||
queues Queue[]
|
||||
|
||||
@@map("tenants")
|
||||
}
|
||||
@@ -379,3 +380,61 @@ model DialplanVersion {
|
||||
@@index([tenantId, context, status])
|
||||
@@map("dialplan_versions")
|
||||
}
|
||||
|
||||
enum QueueStrategy {
|
||||
LONGEST_IDLE_AGENT
|
||||
ROUND_ROBIN
|
||||
TOP_DOWN
|
||||
AGENT_WITH_LEAST_TALK_TIME
|
||||
AGENT_WITH_FEWEST_CALLS
|
||||
SEQUENTIALLY_BY_AGENT_ORDER
|
||||
RING_ALL
|
||||
RING_PROGRESSIVELY
|
||||
|
||||
@@map("queue_strategy")
|
||||
}
|
||||
|
||||
// Tabela tenant-scoped protegida por RLS (agente.md secao 50-51). O nome no
|
||||
// FreeSWITCH é `<queue.id>@<tenant.telephonyDomain>` — mod_callcenter usa
|
||||
// um namespace unico compartilhado entre tenants (nao ha equivalente ao
|
||||
// diretorio por-arquivo do Sofia pra isolar por tenant, ver docs/QUEUES.md).
|
||||
model Queue {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
tenantId String @map("tenant_id") @db.Uuid
|
||||
|
||||
name String
|
||||
description String?
|
||||
|
||||
strategy QueueStrategy @default(LONGEST_IDLE_AGENT)
|
||||
|
||||
mohSound String? @map("moh_sound")
|
||||
announceSound String? @map("announce_sound")
|
||||
announceFrequency Int @default(0) @map("announce_frequency")
|
||||
|
||||
maxWaitTime Int @default(0) @map("max_wait_time")
|
||||
maxWaitTimeWithNoAgent Int @default(0) @map("max_wait_time_with_no_agent")
|
||||
|
||||
agentNoAnswerStatus String? @map("agent_no_answer_status")
|
||||
|
||||
tierRulesApply Boolean @default(false) @map("tier_rules_apply")
|
||||
tierRuleWaitSecond Int @default(300) @map("tier_rule_wait_second")
|
||||
|
||||
discardAbandonedAfter Int @default(60) @map("discard_abandoned_after")
|
||||
abandonedResumeAllowed Boolean @default(false) @map("abandoned_resume_allowed")
|
||||
|
||||
skipAgentsWithExternalCalls Boolean @default(true) @map("skip_agents_with_external_calls")
|
||||
|
||||
recordingEnabled Boolean @default(false) @map("recording_enabled")
|
||||
|
||||
enabled Boolean @default(true)
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
deletedAt DateTime? @map("deleted_at")
|
||||
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id])
|
||||
|
||||
@@unique([tenantId, name])
|
||||
@@index([tenantId])
|
||||
@@map("queues")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user