firstQueueLookup = $firstQueueLookup; $this->queueNameLookup = $queueNameLookup; $this->registry = $registry ?? new FirstQueueRegistry(); } /** * @return array{ * queue_number: ?string, * queue_name: ?string, * current_queue_number: ?string, * language: string, * language_source: string * } */ public static function arabicOnlySelection(): array { return [ 'queue_number' => null, 'queue_name' => null, 'current_queue_number' => null, 'language' => self::LANGUAGE_ARABIC, 'language_source' => self::SOURCE_MULTI_LANGUAGE_DISABLED, ]; } /** * @return array{ * queue_number: ?string, * queue_name: ?string, * current_queue_number: ?string, * language: string, * language_source: string * } */ public function resolve(IClient $client, bool $multiLanguageEnabled = true): array { if (!$multiLanguageEnabled) { return self::arabicOnlySelection(); } $uniqueId = self::stringValue($client->getVariable('UNIQUEID')); $linkedId = self::stringValue($client->getFullVariable('CHANNEL(LINKEDID)')); $currentQueue = self::queueNumber($client); // A later AGI of the same call (e.g. Survey after an agent transfer) runs on a // different channel, so reuse the queue stored for the call's LINKEDID first. $firstQueue = $this->registry->get($linkedId); if ($firstQueue === null) { $firstQueue = $this->firstQueueNumber($uniqueId, $linkedId); if ($firstQueue !== null) { $this->registry->remember($linkedId, $firstQueue); } } $queueName = $firstQueue === null ? null : $this->queueName($firstQueue); $language = self::isEnglish($queueName) ? self::LANGUAGE_ENGLISH : self::LANGUAGE_ARABIC; if ($firstQueue === null) { Log::warning('First queue for the call could not be identified; using the Arabic prompts', [ 'uniqueid' => $uniqueId, 'linkedid' => $linkedId, 'current_queue_number' => $currentQueue, ]); } return [ 'queue_number' => $firstQueue, 'queue_name' => $queueName, 'current_queue_number' => $currentQueue, 'language' => $language, 'language_source' => $firstQueue === null ? self::SOURCE_MISSING : self::SOURCE_FIRST_QUEUE, ]; } /** * Most recent queue on the channel. Diagnostics only: it is overwritten by * every queue the call enters. */ public static function queueNumber(IClient $client): ?string { $queueNumber = $client->getVariable(self::QUEUE_VARIABLE); if (is_int($queueNumber)) { $queueNumber = (string) $queueNumber; } if (!is_string($queueNumber)) { return null; } $queueNumber = trim($queueNumber); return $queueNumber !== '' && ctype_digit($queueNumber) ? $queueNumber : null; } public static function isEnglish(?string $queueName): bool { return $queueName !== null && preg_match('/EN$/i', $queueName) === 1; } private function firstQueueNumber(?string $uniqueId, ?string $linkedId): ?string { if ($uniqueId === null && $linkedId === null) { return null; } try { $lookup = $this->firstQueueLookup ?? static function (?string $uniqueId, ?string $linkedId): ?string { return self::queryFirstQueue($uniqueId, $linkedId); }; $queueNumber = $lookup($uniqueId, $linkedId); return is_string($queueNumber) && $queueNumber !== '' ? $queueNumber : null; } catch (\Throwable $e) { Log::warning('First queue lookup failed', [ 'uniqueid' => $uniqueId, 'linkedid' => $linkedId, 'error' => $e->getMessage(), ]); return null; } } /** * First queue log row of the call: the current channel plus every leg that * shares the LINKEDID and already has a CDR row (i.e. legs that ended * before a transfer). */ private static function queryFirstQueue(?string $uniqueId, ?string $linkedId): ?string { $query = DB::table('asteriskcdrdb.queuelog as q') ->whereIn('q.event', ['DID', 'ENTERQUEUE']) ->where('q.queuename', '<>', 'NONE') ->where('q.queuename', '<>', '') ->where(function ($query) use ($uniqueId, $linkedId) { if ($uniqueId !== null) { $query->where('q.callid', $uniqueId); } if ($linkedId !== null) { $subQuery = function ($subQuery) use ($linkedId) { $subQuery->select('uniqueid') ->from('asteriskcdrdb.cdr') ->where('linkedid', $linkedId); }; if ($uniqueId !== null) { $query->orWhereIn('q.callid', $subQuery); } else { $query->whereIn('q.callid', $subQuery); } } }) ->orderBy('q.id'); $queueNumber = $query->value('q.queuename'); return is_string($queueNumber) ? $queueNumber : null; } private function queueName(string $queueNumber): ?string { try { $lookup = $this->queueNameLookup ?? static function (string $queue): ?string { $name = DB::table('asterisk.queues_config') ->where('extension', $queue) ->value('descr'); return is_string($name) ? $name : null; }; $name = $lookup($queueNumber); return is_string($name) && $name !== '' ? $name : null; } catch (\Throwable $e) { Log::warning('Queue name lookup failed', [ 'queue_number' => $queueNumber, 'error' => $e->getMessage(), ]); return null; } } private static function stringValue($value): ?string { if (is_int($value)) { $value = (string) $value; } if (!is_string($value)) { return null; } $value = trim($value); return $value === '' ? null : $value; } }