cal_core/
account.rs

1// Filename cal-core/src/account.rs
2
3use crate::accounting::Address;
4use crate::core::*;
5use crate::device::device::{Connector, Device, DeviceStruct};
6use crate::utils::*;
7use crate::RecordReference;
8use bson::DateTime;
9use phonenumber::country;
10use phonenumber::country::Id;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::fmt::{Debug, Formatter};
14#[cfg(feature = "openapi")]
15use utoipa::ToSchema;
16#[derive(Serialize, Deserialize, Clone, Debug)]
17#[cfg_attr(feature = "openapi", derive(ToSchema))]
18#[serde(rename_all = "camelCase")]
19pub struct AccountLite {
20    #[serde(deserialize_with = "crate::shared::object_id_as_string", rename = "_id")]
21    pub id: String,
22    pub name: String,
23    pub mbn: String,
24    pub domain: String,
25    #[serde(default = "crate::shared::build_timestamp")]
26    pub created: DateTime,
27    pub organisation: RecordReference,
28    #[serde(default)]
29    pub teams: TeamsAccount,
30    #[serde(default)]
31    pub environment: Environment,
32    #[serde(default)]
33    pub spend_cap: SpendCap,
34    #[serde(default)]
35    pub concurrency: Concurrency,
36    #[serde(default)]
37    pub class_of_service: ClassOfService,
38    pub cluster_settings: Cluster,
39}
40
41
42
43impl AccountLite {
44    pub fn country_code(&self) -> &str {
45        self.environment.country_code.as_str()
46    }
47
48    pub fn country_code_parsed(&self) -> Id {
49        self.environment.country_code_parsed()
50    }
51
52    pub fn clean_str(&self, str: &str) -> String {
53        clean_str(self.environment.country_code_parsed(), str)
54    }
55}
56
57#[derive(Serialize, Deserialize, Clone, Debug)]
58#[cfg_attr(feature = "openapi", derive(ToSchema))]
59#[serde(rename_all = "camelCase")]
60pub struct Account {
61    #[serde(deserialize_with = "crate::shared::object_id_as_string", rename = "_id")]
62    pub id: String,
63    pub name: String,
64    pub mbn: String,
65    pub domain: String,
66    #[serde(default = "crate::shared::build_timestamp")]
67    pub created: DateTime,
68    pub organisation: RecordReference,
69    #[serde(default)]
70    pub ddis: Vec<DDI>,
71    #[serde(default)]
72    pub trunks: Vec<Trunk>,
73    #[serde(default)]
74    #[serde(rename = "callbackURLS")]
75    pub hooks: Vec<Hook>,
76    #[serde(default)]
77    pub devices: Vec<DeviceStruct>,
78    #[serde(default)]
79    pub assets: Vec<Asset>,
80    #[serde(default)]
81    pub address: Address,
82    #[serde(default)]
83    pub addresses: Vec<Address>,
84    #[serde(default)]
85    pub teams: TeamsAccount,
86    #[serde(default)]
87    pub environment: Environment,
88    #[serde(default)]
89    pub spend_cap: SpendCap,
90    #[serde(default)]
91    pub concurrency: Concurrency,
92    #[serde(default)]
93    pub class_of_service: ClassOfService,
94    pub cluster_settings: Cluster,
95    #[serde(default)]
96    pub api_keys: APIKeys,
97}
98
99impl From<Account> for AccountLite {
100    fn from(account: Account) -> Self {
101        AccountLite {
102            id: account.id,
103            name: account.name,
104            mbn: account.mbn,
105            domain: account.domain,
106            created: account.created,
107            organisation: account.organisation,
108            teams: account.teams,
109            environment: account.environment,
110            spend_cap: account.spend_cap,
111            concurrency: account.concurrency,
112            class_of_service: account.class_of_service,
113            cluster_settings: account.cluster_settings,
114        }
115    }
116}
117
118// Implement From for references too, which is often useful
119impl From<&Account> for AccountLite {
120    fn from(account: &Account) -> Self {
121        AccountLite {
122            id: account.id.clone(),
123            name: account.name.clone(),
124            mbn: account.mbn.clone(),
125            domain: account.domain.clone(),
126            created: account.created.clone(),
127            organisation: account.organisation.clone(),
128            teams: account.teams.clone(),
129            environment: account.environment.clone(),
130            spend_cap: account.spend_cap.clone(),
131            concurrency: account.concurrency.clone(),
132            class_of_service: account.class_of_service.clone(),
133            cluster_settings: account.cluster_settings.clone(),
134        }
135    }
136}
137
138#[derive(Serialize, Deserialize, Clone, Debug)]
139#[serde(rename_all = "camelCase")]
140pub struct Cluster {
141    #[serde(deserialize_with = "crate::shared::object_id_as_string", rename = "_id")]
142    pub id: String,
143    pub name: String,
144    pub server_a: String,
145}
146
147#[derive(Serialize, Deserialize, Clone, Debug)]
148#[serde(rename_all = "camelCase")]
149pub struct APIKeys {
150    #[serde(default)]
151    keys: Vec<APIKey>,
152}
153
154impl Default for APIKeys {
155    fn default() -> Self {
156        Self { keys: Vec::new() }
157    }
158}
159
160#[derive(Serialize, Deserialize, Clone, Debug)]
161#[serde(rename_all = "camelCase")]
162pub struct APIKey {
163    key: String,
164}
165
166#[derive(Serialize, Deserialize, Clone, Debug)]
167#[serde(rename_all = "camelCase")]
168pub struct Environment {
169    #[serde(default = "build_logger")]
170    pub logger: LoggerLevel,
171    #[serde(rename = "dialTimeLimit")]
172    #[serde(default = "build_call_time")]
173    pub max_call_time: u16,
174    #[serde(rename = "dialTimeout")]
175    #[serde(default = "build_ring_time")]
176    pub ring_time: u8,
177    #[serde(default = "build_tts_vendor")]
178    pub tts_vendor: String,
179    #[serde(default = "build_tts_language")]
180    #[serde(rename = "ttsLanguageV2")]
181    pub tts_language: String,
182    #[serde(default = "build_tts_voice")]
183    pub tts_voice: String,
184    #[serde(default = "build_asr_vendor")]
185    pub asr_vendor: AsrVendor,
186    #[serde(default = "build_asr_language")]
187    pub asr_language: String,
188    #[serde(default = "build_default_timezone")]
189    pub time_zone: String,
190    #[serde(default = "build_country_code")]
191    pub country_code: String,
192    #[serde(default = "build_record_retention")]
193    pub recording_retention: String,
194    pub ingress_script: Option<String>,
195}
196
197impl Environment {
198    pub fn country_code_parsed(&self) -> Id {
199        self.country_code.parse().unwrap_or_else(|_| country::GB)
200    }
201}
202impl Default for Environment {
203    fn default() -> Self {
204        Self {
205            logger: LoggerLevel::Warn,
206            max_call_time: DEFAULT_CALL_TIME,
207            ring_time: DEFAULT_RING_TIME,
208            tts_vendor: DEFAULT_TTS_VENDOR.to_string(),
209            tts_language: DEFAULT_TTS_LANGUAGE.to_string(),
210            tts_voice: DEFAULT_TTS_VOICE.to_string(),
211            asr_vendor: AsrVendor::Google,
212            asr_language: DEFAULT_ASR_LANGUAGE.to_string(),
213            time_zone: DEFAULT_TIMEZONE.to_string(),
214            country_code: DEFAULT_COUNTRY_CODE.to_string(),
215            recording_retention: DEFAULT_RECORDING_RETENTION.to_string(),
216            ingress_script: None,
217        }
218    }
219}
220
221#[derive(Serialize, Deserialize, Clone, Debug)]
222#[serde(rename_all = "camelCase")]
223pub struct SpendCap {
224    #[serde(default = "build_spend_cap_value")]
225    value: u16,
226    #[serde(default = "build_spend_cap_level")]
227    warn: u8,
228    #[serde(default = "build_spend_cap_action")]
229    action: RouteAction,
230    #[serde(default)]
231    email_recipients: Vec<String>,
232}
233
234impl Default for SpendCap {
235    fn default() -> Self {
236        Self {
237            value: 100,
238            warn: 80,
239            action: RouteAction::Warn,
240            email_recipients: Vec::new(),
241        }
242    }
243}
244
245#[derive(Serialize, Deserialize, Clone, Debug)]
246#[serde(rename_all = "camelCase")]
247pub struct Concurrency {
248    #[serde(rename = "type")]
249    #[serde(default = "build_license")]
250    license: Licence,
251    #[serde(default = "build_concurrency_action")]
252    action: RouteAction,
253    #[serde(default)]
254    email_recipients: Vec<String>,
255}
256
257impl Default for Concurrency {
258    fn default() -> Self {
259        Concurrency {
260            license: Licence::Standard,
261            action: RouteAction::Warn,
262            email_recipients: vec![],
263        }
264    }
265}
266
267#[derive(Serialize, Deserialize, Clone, Debug)]
268#[serde(rename_all = "camelCase")]
269pub struct ClassOfService {
270    #[serde(rename = "type")]
271    #[serde(default = "build_cos_action")]
272    license: COSAction,
273    #[serde(default)]
274    countries: Vec<String>,
275    #[serde(default)]
276    prefix_exceptions: String,
277    #[serde(rename = "maxPPM")]
278    #[serde(default = "build_cos_ppm")]
279    max_ppm: f32,
280    #[serde(rename = "maxPPC")]
281    #[serde(default = "build_cos_ppc")]
282    max_ppc: f32,
283}
284
285impl Default for ClassOfService {
286    fn default() -> Self {
287        Self {
288            license: COSAction::Allow,
289            countries: Vec::new(),
290            prefix_exceptions: String::from(""),
291            max_ppc: 10.00,
292            max_ppm: 10.00,
293        }
294    }
295}
296
297#[derive(Serialize, Deserialize, Clone, Debug)]
298#[serde(rename_all = "camelCase")]
299pub enum LoggerLevel {
300    Debug,
301    Info,
302    Warn,
303    Error,
304}
305
306#[derive(Serialize, Deserialize, Clone, Debug)]
307#[serde(rename_all = "camelCase")]
308pub enum Licence {
309    Standard,
310    Professional,
311    Enterprise,
312}
313
314#[derive(Serialize, Deserialize, Clone, Debug)]
315pub enum COSAction {
316    #[serde(alias = "DENY")]
317    #[serde(rename = "deny")]
318    Deny,
319    #[serde(alias = "ALLOW")]
320    #[serde(rename = "allow")]
321    Allow,
322    #[serde(alias = "WARN")]
323    #[serde(rename = "warn")]
324    Warn,
325}
326
327#[derive(Serialize, Deserialize, Clone, Debug)]
328pub enum RouteAction {
329    #[serde(rename = "warn")]
330    Warn,
331    #[serde(rename = "restrict")]
332    Restrict,
333    #[serde(rename = "deny")]
334    Deny,
335}
336
337#[derive(Serialize, Deserialize, Clone, Debug)]
338#[serde(rename_all = "camelCase")]
339pub struct TeamsAccount {
340    #[serde(rename = "type")]
341    pub domain_type: TeamsDomainType,
342    #[serde(default)]
343    pub domain: String,
344    pub txt: Option<String>,
345    // #[serde(rename = "serverA")]
346    // pub server_a: String,
347    // #[serde(rename = "serverB")]
348    // pub server_b: String,
349}
350
351#[derive(Serialize, Deserialize, Clone, Debug)]
352#[serde(rename_all = "camelCase")]
353pub enum TeamsDomainType {
354    Callable,
355    Legacy,
356}
357
358impl Default for TeamsAccount {
359    fn default() -> Self {
360        Self {
361            domain_type: TeamsDomainType::Callable,
362            domain: String::from(""),
363            txt: None,
364        }
365    }
366}
367
368
369#[derive(Serialize, Deserialize, Clone, Debug)]
370#[serde(rename_all = "camelCase")]
371pub struct Asset {
372    #[serde(deserialize_with = "crate::shared::object_id_as_string", rename = "_id")]
373    pub id: String,
374    pub bucket: String,
375    pub key: String,
376    pub filename: String,
377    pub name: String,
378    pub mime_type: String,
379    pub size: u32,
380    #[serde(default = "crate::shared::build_timestamp")]
381    pub created: DateTime,
382    #[serde(default)]
383    pub meta: HashMap<String, String>,
384}
385
386#[derive(Serialize, Deserialize, Clone, Debug)]
387#[serde(rename_all = "camelCase")]
388pub struct Setting {
389    #[serde(default)]
390    #[serde(deserialize_with = "crate::shared::object_id_as_string", rename = "_id")]
391    pub id: String,
392    pub key: String,
393    #[serde(rename = "type")]
394    pub setting_type: String,
395    pub value: String,
396}
397
398// File: cal-core/src/account.rs (DDI struct section)
399
400#[derive(Serialize, Deserialize, Debug, Clone)]
401#[cfg_attr(feature = "openapi", derive(ToSchema))]
402#[serde(rename_all = "camelCase")]
403pub struct DDI {
404    #[serde(deserialize_with = "crate::shared::object_id_as_string", rename = "_id")]
405    pub id: String,
406    pub name: String,
407    #[serde(default)]
408    pub meta: HashMap<String, String>,
409
410    // Additional fields from the payload
411    #[serde(skip_serializing_if = "Option::is_none")]
412    pub classification: Option<String>,
413    #[serde(skip_serializing_if = "Option::is_none")]
414    pub ticket_id: Option<String>,
415    #[serde(skip_serializing_if = "Option::is_none")]
416    pub global_id: Option<String>,
417    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
418    pub ddi_type: Option<String>,
419    #[serde(default)]
420    pub features: Vec<String>,
421    #[serde(skip_serializing_if = "Option::is_none")]
422    pub created: Option<DateTime>,
423
424    // WhatsApp-specific fields
425    #[serde(skip_serializing_if = "Option::is_none")]
426    pub dialog_channel: Option<DialogChannel>,
427    #[serde(skip_serializing_if = "Option::is_none")]
428    pub dialog_key: Option<DialogKey>,
429    #[serde(skip_serializing_if = "Option::is_none")]
430    pub whats_app_account: Option<WhatsAppAccount>,
431}
432
433// Supporting structures for WhatsApp DDIs
434#[derive(Serialize, Deserialize, Debug, Clone)]
435#[serde(rename_all = "camelCase")]
436pub struct DialogChannel {
437    #[serde(rename = "_id")]
438    pub id: String,
439    pub setup_info: SetupInfo,
440    pub status: String,
441    pub account_mode: String,
442    pub created_at: String,
443    pub client_id: String,
444    pub client: DialogClient,
445    pub waba_account: WabaAccount,
446    pub integration: Integration,
447    pub current_limit: Option<String>,
448    pub current_quality_rating: String,
449    pub hub_status: String,
450    pub is_migrated: bool,
451    pub is_oba: bool,
452    pub version: i32,
453}
454
455#[derive(Serialize, Deserialize, Debug, Clone)]
456#[serde(rename_all = "camelCase")]
457pub struct SetupInfo {
458    pub phone_number: String,
459    pub phone_name: String,
460}
461
462#[derive(Serialize, Deserialize, Debug, Clone)]
463#[serde(rename_all = "camelCase")]
464pub struct DialogClient {
465    #[serde(rename = "_id")]
466    pub id: String,
467    pub name: String,
468    pub contact_info: ContactInfo,
469}
470
471#[derive(Serialize, Deserialize, Debug, Clone)]
472pub struct ContactInfo {
473    pub email: String,
474}
475
476#[derive(Serialize, Deserialize, Debug, Clone)]
477#[serde(rename_all = "camelCase")]
478pub struct WabaAccount {
479    #[serde(rename = "_id")]
480    pub id: String,
481    pub on_behalf_of_business_info: OnBehalfOfBusinessInfo,
482    pub fb_account_status: String,
483    pub external_id: String,
484    pub fb_business_id: String,
485}
486
487#[derive(Serialize, Deserialize, Debug, Clone)]
488#[serde(rename_all = "camelCase")]
489pub struct OnBehalfOfBusinessInfo {
490    #[serde(rename = "_id")]
491    pub id: String,
492    pub name: String,
493    #[serde(rename = "type")]
494    pub business_type: String,
495    pub status: String,
496}
497
498#[derive(Serialize, Deserialize, Debug, Clone)]
499#[serde(rename_all = "camelCase")]
500pub struct Integration {
501    pub app_id: String,
502    pub hosting_platform_type: String,
503    pub enabled: bool,
504    pub state: String,
505}
506
507#[derive(Serialize, Deserialize, Debug, Clone)]
508#[serde(rename_all = "camelCase")]
509pub struct DialogKey {
510    #[serde(rename = "_id")]
511    pub id: String,
512    pub address: String,
513    pub api_key: String,
514    pub app_id: String,
515}
516
517#[derive(Serialize, Deserialize, Debug, Clone)]
518#[serde(rename_all = "camelCase")]
519pub struct WhatsAppAccount {
520    pub account: WhatsAppAccountDetails,
521    pub phone_number: WhatsAppPhoneNumber,
522}
523
524#[derive(Serialize, Deserialize, Debug, Clone)]
525#[serde(rename_all = "camelCase")]
526pub struct WhatsAppAccountDetails {
527    #[serde(rename = "_id")]
528    pub id: String,
529    pub name: String,
530    pub timezone_id: String,
531    pub message_template_namespace: String,
532    pub on_behalf_of: Option<OnBehalfOf>,
533    pub phone_numbers: PhoneNumbers,
534}
535
536#[derive(Serialize, Deserialize, Debug, Clone)]
537#[serde(rename_all = "camelCase")]
538pub struct OnBehalfOf {
539    #[serde(rename = "_id")]
540    pub id: String,
541    pub name: String,
542    pub status: String,
543}
544
545#[derive(Serialize, Deserialize, Debug, Clone)]
546pub struct PhoneNumbers {
547    pub data: Vec<WhatsAppPhoneNumber>,
548}
549
550#[derive(Serialize, Deserialize, Debug, Clone)]
551#[serde(rename_all = "camelCase")]
552pub struct WhatsAppPhoneNumber {
553    #[serde(rename = "_id")]
554    pub id: String,
555    pub verified_name: String,
556    pub code_verification_status: String,
557    pub display_phone_number: String,
558    pub quality_rating: String,
559    pub last_onboarded_time: Option<String>,
560    pub platform_type: String,
561    pub throughput: Throughput,
562}
563
564#[derive(Serialize, Deserialize, Debug, Clone)]
565pub struct Throughput {
566    pub level: String,
567}
568
569#[derive(Serialize, Deserialize, Clone, Debug)]
570#[serde(rename_all = "camelCase")]
571pub struct Trunk {
572    #[serde(deserialize_with = "crate::shared::object_id_as_string", rename = "_id")]
573    pub id: String,
574    pub ip: String,
575    #[serde(default = "build_trunk_description")]
576    pub description: String,
577    #[serde(deserialize_with = "crate::shared::from_str", default = "build_trunk_port")]
578    pub port: u16,
579}
580
581
582
583#[derive(Serialize, Deserialize, Clone, Debug)]
584#[serde(rename_all = "camelCase")]
585pub struct Hook {
586    #[serde(deserialize_with = "crate::shared::object_id_as_string", rename = "_id")]
587    pub id: String,
588    pub app: String,
589    pub url: String,
590    #[serde(default = "AuthType::default_method")]
591    pub auth_type: AuthType,
592    #[serde(default = "HookMethod::default_method")]
593    pub method: HookMethod,
594    #[serde(default)]
595    pub meta: HashMap<String, String>,
596    #[serde(default)]
597    pub api_key_name: String,
598    #[serde(default)]
599    pub api_key_value: String,
600    #[serde(default)]
601    pub api_secret_name: String,
602    #[serde(default)]
603    pub api_secret_value: String,
604}
605
606#[derive(Serialize, Deserialize, Clone)]
607pub enum HookMethod {
608    #[serde(rename = "POST")]
609    Post,
610    #[serde(rename = "GET")]
611    Get,
612}
613
614impl Debug for HookMethod {
615    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
616        match self {
617            HookMethod::Post => write!(f, "HookMethod::Post"),
618            HookMethod::Get => write!(f, "HookMethod::Get"),
619        }
620    }
621}
622
623
624impl HookMethod {
625    fn default_method() -> Self {
626        HookMethod::Post
627    }
628}
629
630#[derive(Serialize, Deserialize, Clone, Debug)]
631pub enum AuthType {
632    #[serde(rename = "DISABLED")]
633    Disabled,
634    #[serde(rename = "KEY")]
635    Key,
636    #[serde(rename = "KEY_SECRET")]
637    KeySecret,
638    #[serde(rename = "BASIC_AUTH")]
639    BasicAuth,
640    #[serde(rename = "OAUTH")]
641    OAuth,
642}
643
644impl AuthType {
645    fn default_method() -> Self {
646        AuthType::Disabled
647    }
648}
649
650impl Connector for Device {
651    fn get_connect_to(&mut self, state: &mut FlowState) -> ConnectState {
652        match self {
653            Device::InboundFlow(value) => value.get_connect_to(state),
654            Device::OutboundFlow(value) => value.get_connect_to(state),
655            Device::AppFlow(value) => value.get_connect_to(state),
656            Device::WhatsAppFlow(value) => value.get_connect_to(state),
657            Device::AniRouter(value) => value.get_connect_to(state),
658            Device::DnisRouter(value) => value.get_connect_to(state),
659            Device::DigitRouter(value) => value.get_connect_to(state),
660            Device::TimeRangeRouter(value) => value.get_connect_to(state),
661            Device::DayOfWeekRouter(value) => value.get_connect_to(state),
662            Device::DateRangeRouter(value) => value.get_connect_to(state),
663            Device::Play(value) => value.get_connect_to(state),
664            Device::Say(value) => value.get_connect_to(state),
665            Device::Voicemail(value) => value.get_connect_to(state),
666            Device::Script(value) => value.get_connect_to(state),
667            Device::Queue(value) => value.get_connect_to(state),
668            Device::Sms(value) => value.get_connect_to(state),
669            Device::Email(value) => value.get_connect_to(state),
670            Device::Tag(value) => value.get_connect_to(state),
671            Device::TagRouter(value) => value.get_connect_to(state),
672            Device::Service(value) => value.get_connect_to(state),
673            Device::Client(value) => value.get_connect_to(state),
674            Device::Teams(value) => value.get_connect_to(state),
675            Device::Remote(value) => value.get_connect_to(state),
676            Device::HuntGroup(value) => value.get_connect_to(state),
677            Device::SipGateway(value) => value.get_connect_to(state),
678            Device::SipExtension(value) => value.get_connect_to(state),
679            _ => ConnectState::device_error(HANG_UP_CONNECT),
680        }
681    }
682}
683
684#[derive(Serialize, Deserialize, Clone)]
685#[serde(rename_all = "camelCase")]
686pub enum AsrVendor {
687    Deepgram,
688    Google,
689    Aws,
690    Ibm,
691    Microsoft,
692    Nuance,
693    Nvidia,
694    Soniox,
695}
696
697fn build_asr_vendor() -> AsrVendor {
698    AsrVendor::Google
699}
700
701fn build_asr_language() -> String {
702    String::from(DEFAULT_ASR_LANGUAGE)
703}
704
705fn build_tts_vendor() -> String {
706    String::from(DEFAULT_TTS_VENDOR)
707}
708
709fn build_tts_language() -> String {
710    String::from(DEFAULT_TTS_LANGUAGE)
711}
712
713fn build_tts_voice() -> String {
714    String::from(DEFAULT_TTS_VOICE)
715}
716
717fn build_default_timezone() -> String {
718    String::from(DEFAULT_TIMEZONE)
719}
720
721fn build_trunk_description() -> String {
722    String::from(DEFAULT_TRUNK_DESCRIPTION)
723}
724
725fn build_trunk_port() -> u16 {
726    DEFAULT_TRUNK_PORT
727}
728
729fn build_country_code() -> String {
730    String::from(DEFAULT_COUNTRY_CODE)
731}
732
733fn build_logger() -> LoggerLevel {
734    LoggerLevel::Warn
735}
736
737fn build_call_time() -> u16 {
738    DEFAULT_CALL_TIME
739}
740
741fn build_ring_time() -> u8 {
742    DEFAULT_RING_TIME
743}
744
745fn build_spend_cap_value() -> u16 {
746    100
747}
748
749fn build_spend_cap_level() -> u8 {
750    80
751}
752
753fn build_spend_cap_action() -> RouteAction {
754    RouteAction::Warn
755}
756
757fn build_concurrency_action() -> RouteAction {
758    RouteAction::Warn
759}
760
761fn build_cos_action() -> COSAction {
762    COSAction::Allow
763}
764
765fn build_cos_ppm() -> f32 {
766    15.00
767}
768
769fn build_cos_ppc() -> f32 {
770    15.00
771}
772
773fn build_license() -> Licence {
774    Licence::Standard
775}
776
777fn build_record_retention() -> String {
778    String::from(DEFAULT_RECORDING_RETENTION)
779}
780
781
782
783