moh: music-on-hold classes, uploads, pickers for users and queues
/api/moh-classes: create (slug, not "default"), delete (409 while an extension or queue uses it), add/play/remove files. Files are 8 kHz wav in MOH_DIR/<class>/ and sound_files rows of kind moh. Extensions and queues may only pick default or an existing class. Two things found with real calls, written back into 03 and 04: - An empty class plays silence on Asterisk 24. Only classes with music are rendered, so a channel asking for an empty one gets default. - The generator reloads only changed files, and a file upload changes no config (except a class's first/last). The route sends moh reload after every file change. Web: a Music on hold section on Sounds & music, and a select of sets in the user drawer's Phone tab and the queue form. load-golden makes the class folders like the api does. Golden case queue gains two classes. e2e m8-t5: set made in the UI, upload listed by Asterisk, a queue caller hears it, a user's moh_suggest, delete refused while used, an emptied set falls back to default. Task: M8-T5
This commit is contained in:
1 parent
e95271e694
commit
b8ff6e3386
34 files changed
+840
-21
No files matched your search
@@ -10,6 +10,7 @@ import {
|
||||
extensions,
|
||||
holidays,
|
||||
ivrOptions,
|
||||
mohClasses,
|
||||
pagingGroupMembers,
|
||||
pagingGroups,
|
||||
ivrs,
|
||||
@@ -195,6 +196,11 @@ export type Snapshot = {
|
||||
timeConditions: SnapTimeCondition[];
|
||||
holidays: SnapHoliday[];
|
||||
announcements: SnapAnnouncement[];
|
||||
/**
|
||||
* Music-on-hold classes that have music ('default' is built in and not here). An empty class
|
||||
* would play silence; left out, Asterisk falls back to default (seen in M8-T5).
|
||||
*/
|
||||
mohClasses: string[];
|
||||
/** Played before recording a call that a phone number records (Settings → Recording). */
|
||||
recordingAnnouncement: string | null;
|
||||
};
|
||||
@@ -254,6 +260,10 @@ export async function loadSnapshot(db: Db, now = new Date()): Promise<Snapshot>
|
||||
const tcRows = await db.select().from(timeConditions);
|
||||
const holidayRows = await db.select().from(holidays);
|
||||
const annRows = await db.select().from(announcements);
|
||||
const mohRows = await db
|
||||
.selectDistinct({ name: mohClasses.name })
|
||||
.from(mohClasses)
|
||||
.innerJoin(soundFiles, eq(soundFiles.mohClassId, mohClasses.id));
|
||||
const optionRows = await db.select().from(ivrOptions);
|
||||
const soundName = new Map(
|
||||
(await db.select({ id: soundFiles.id, name: soundFiles.asteriskName }).from(soundFiles)).map(
|
||||
@@ -352,6 +362,7 @@ export async function loadSnapshot(db: Db, now = new Date()): Promise<Snapshot>
|
||||
maxMembers: c.maxMembers,
|
||||
record: c.record,
|
||||
})),
|
||||
mohClasses: mohRows.map((m) => m.name),
|
||||
pagingGroups: pageRows.map((g) => ({
|
||||
id: g.id,
|
||||
number: g.number,
|
||||
|
||||
Reference in new issue
Block a user