id) ->join('user_extensions', 'users.id', '=', 'user_extensions.user_id') ->where('user_extensions.sip_credentials_enabled', true) ->orderBy('user_extensions.id', 'DESC') ->first(); // --- Three-tier SIP data resolution --- // 1. Per-user UserExtension row (highest priority) // 2. Company-level pbxWssUrl setting (protocol=wss, port=8089, extension=user_id) // 3. Server default via url('') (lowest priority) $companyId = $user->company_id; $globalWssUrl = $companyId ? CompanySettings::get( SettingsHelper::S_PBX_WSS_URL, null, $companyId ) : null; $globalWssPort = $companyId ? CompanySettings::get( SettingsHelper::S_PBX_WSS_PORT, null, $companyId ) : null; if ($wssExt && $wssExt->label && $wssExt->sip_domain) { // Tier 1: Per-user extension data $extension = $wssExt->label; $pbx_url = $wssExt->sip_domain; $ws_port = $wssExt->sip_port ?? 8089; } elseif (!empty($globalWssUrl)) { // Tier 2: Company-level WSS settings (extension = user id) $extension = $user->id; $pbx_url = $globalWssUrl; $ws_port = !empty($globalWssPort) ? $globalWssPort : 8089; } else { // Tier 3: Default server fallback $extension = '99' . $user->id; $pbx_url = url(''); $ws_port = env('WEBSOCKETPORT', 8089); } $sip_extension = $extension; $sipRow = \DB::table('asterisk.sip')->where(['keyword' => 'secret', 'id' => $extension])->first(); $sip_password = $sipRow ? $sipRow->data : null; // Ensure pbx_url is a bare hostname (strip scheme if present) if (str_contains($pbx_url, '://')) { $pbx_url_parts = explode('://', $pbx_url); $pbx_url = end($pbx_url_parts); } $description = 'This is a stable version of the phone. If you are facing any issue with the phone, please use this version.'; return [ 'user' => [ 'name' => $user->name, 'id' => $user->id, 'email' => $user->email, 'timezone' => $user->time_zone, 'company_name' => $user->company->name ?? "Main Company", ], 'description' => $description, 'extension' => $sip_extension, 'password' => $sip_password, 'pbx_url' => $pbx_url, 'port' => $ws_port, 'path' => '/ws', 'getCallerInfoUrl' => route('getCallerInfo'), 'autoHoldEnabled' => (bool) CompanySettings::get('S_COMPANY_SOFTPHONE_AUTO_HOLD'), 'autoAnswer' => (bool) CompanySettings::get('S_COMPANY_SOFTPHONE_AUTO_ANSWER'), 'autoAnswerTimeout' => SettingsHelper::normalizeSoftphoneAutoAnswerTimeout(CompanySettings::get(SettingsHelper::S_COMPANY_SOFTPHONE_AUTO_ANSWER_TIMEOUT)), 'surveyTransferEnabled' => (bool) CompanySettings::get(SettingsHelper::S_COMPANY_SOFTPHONE_SURVEY_TRANSFER), 'glnEnabled' => (bool) CompanySettings::get(SettingsHelper::S_COMPANY_GLN_ENABLED, null, $companyId), 'activeLinkedidUrl' => route('dashboard.activeLinkedid'), 'pauseEnablesDnd' => (bool) CompanySettings::get(SettingsHelper::S_COMPANY_SOFTPHONE_PAUSE_ENABLE_DND), 'whatsAppCountryCode' => SettingsHelper::normalizeWhatsAppCountryCode(CompanySettings::get(SettingsHelper::S_COMPANY_WHATSAPP_COUNTRY_CODE)), 'dndPolicy' => $this->resolveDndPolicy($user), ]; } /** * Returns "disabled" when DND is off for the user, otherwise "allow". */ public function resolveDndPolicy(User $user): string { if (isset($user->dnd_enabled) && !$user->dnd_enabled) { return 'disabled'; } return 'allow'; } }