'templates.json', 'es' => 'templates_es.json'] */ public function getAvailableDefaultLanguages(): array { $path = $this->getDefaultTemplatesPath(); if (!is_dir($path)) { return []; } $languages = []; $files = glob($path . 'templates_*.json'); foreach ($files as $file) { if (preg_match('/templates_([a-z]{2})\.json$/', $file, $matches)) { $languages[$matches[1]] = $file; } } return $languages; } /** * Load default templates for a given language. * * @param string $language Language code (e.g. 'en', 'es') * @return array */ public function getDefaultTemplates(string $language = 'en'): array { $languages = $this->getAvailableDefaultLanguages(); if (!isset($languages[$language])) { return []; } $filePath = $languages[$language]; if (!file_exists($filePath)) { return []; } $content = file_get_contents($filePath); $templates = json_decode($content, true); if (!is_array($templates)) { return []; } // Add language and key to each template $result = []; foreach ($templates as $key => $template) { $template['key'] = $key; $template['language'] = $language; $result[$key] = $template; } return $result; } /** * Load default templates for all available languages. * * @return array Keyed by language code */ public function getAllDefaultTemplates(): array { $languages = $this->getAvailableDefaultLanguages(); $all = []; foreach ($languages as $langCode => $filename) { $all[$langCode] = $this->getDefaultTemplates($langCode); } return $all; } /** * Build the Twilio Content API payload for a single template. * * @param array $template Template definition from JSON * @return array Payload ready for POST /v1/Content */ protected function buildContentApiPayload(array $template): array { $type = $template['type'] ?? 'text'; $body = $template['body'] ?? ''; $variables = $template['variables'] ?? []; // Build the types object based on template type $types = []; if ($type === 'quick-reply') { $actions = []; foreach ($template['buttons'] ?? [] as $button) { $actions[] = [ 'type' => 'QUICK_REPLY', 'title' => $button['title'], 'id' => $button['id'], ]; } $types['twilio/quick-reply'] = [ 'body' => $body, 'actions' => $actions, ]; } elseif ($type === 'call-to-action') { $actions = []; foreach ($template['buttons'] ?? [] as $button) { $action = [ 'type' => $button['type'] ?? 'URL', 'title' => $button['title'], ]; if (!empty($button['url'])) { $action['url'] = $button['url']; } if (!empty($button['phone'])) { $action['phone'] = $button['phone']; } $actions[] = $action; } $types['twilio/call-to-action'] = [ 'body' => $body, 'actions' => $actions, ]; } else { // Default: text $types['twilio/text'] = [ 'body' => $body, ]; } // Build variables object for Content API // Using named keys to match {{name}}, {{restaurant}}, etc. in body $exampleValues = $template['example_values'] ?? []; $contentVariables = []; foreach ($variables as $varName => $mappedField) { $contentVariables[$varName] = [ 'key' => $varName, 'example' => $exampleValues[$varName] ?? $varName, ]; } $payload = [ 'friendly_name' => $template['template_name'] ?? $template['name'] ?? 'template', 'language' => $template['language'] ?? 'en', 'types' => $types, ]; if (!empty($contentVariables)) { $payload['variables'] = $contentVariables; } ray($payload); return $payload; } /** * Create a single template in Twilio Content API. * * @param array $template Template definition from JSON * @return array Result with success, sid, message */ public function createContentTemplate(array $template): array { if (!$this->isConfigured()) { return $this->errorResponse(__eva('WhatsApp service is not configured')); } $payload = $this->buildContentApiPayload($template); try { $response = $this->contentClient->post('v1/Content', [ 'json' => $payload, ]); $statusCode = $response->getStatusCode(); $body = json_decode($response->getBody()->getContents(), true); if ($statusCode >= 200 && $statusCode < 300) { return $this->successResponse(__eva('Template created successfully'), [ 'sid' => $body['sid'] ?? null, 'friendly_name' => $body['friendly_name'] ?? null, 'language' => $body['language'] ?? null, 'date_created' => $body['date_created'] ?? null, ]); } return $this->errorResponse( $body['message'] ?? __eva('Failed to create template'), [ 'error_code' => $body['code'] ?? null, 'error_message' => $body['message'] ?? null, 'details' => $body['details'] ?? null, ] ); } catch (GuzzleException $e) { $this->logError('Create content template failed', ['error' => $e->getMessage()]); return $this->errorResponse($e->getMessage()); } } /** * Request WhatsApp approval for a Content template. * * @param string $contentSid The Content SID (HXxxxx) * @param string $templateName The template name for WhatsApp * @param string $category Template category (UTILITY, MARKETING, AUTHENTICATION) * @return array */ public function requestWhatsappApproval(string $contentSid, string $templateName, string $category = 'UTILITY'): array { if (!$this->isConfigured()) { return $this->errorResponse(__eva('WhatsApp service is not configured')); } try { $payload = [ 'name' => $templateName, 'category' => $category, ]; $response = $this->contentClient->post("v1/Content/{$contentSid}/ApprovalRequests/whatsapp", [ 'json' => $payload, ]); $statusCode = $response->getStatusCode(); $body = json_decode($response->getBody()->getContents(), true); if ($statusCode >= 200 && $statusCode < 300) { return $this->successResponse(__eva('Approval requested successfully'), [ 'status' => $body['status'] ?? 'submitted', 'category' => $body['category'] ?? $category, ]); } return $this->errorResponse( $body['message'] ?? __eva('Failed to request approval'), [ 'error_code' => $body['code'] ?? null, 'error_message' => $body['message'] ?? null, ] ); } catch (GuzzleException $e) { $this->logError('Request WhatsApp approval failed', ['error' => $e->getMessage()]); return $this->errorResponse($e->getMessage()); } } /** * Create a template in Twilio and request WhatsApp approval in one step. * * @param array $template Template definition from JSON * @return array */ public function createAndSubmitTemplate(array $template): array { // Step 1: Create in Twilio Content API $createResult = $this->createContentTemplate($template); if (!$createResult['success']) { return $createResult; } $contentSid = $createResult['data']['sid']; $templateName = $template['template_name'] ?? $template['name'] ?? 'template'; $category = $template['category'] ?? 'UTILITY'; // Step 2: Request WhatsApp approval $approvalResult = $this->requestWhatsappApproval($contentSid, $templateName, $category); return $this->successResponse(__eva('Template created and submitted for approval'), [ 'sid' => $contentSid, 'friendly_name' => $createResult['data']['friendly_name'] ?? null, 'template_name' => $templateName, 'language' => $createResult['data']['language'] ?? null, 'approval_status' => $approvalResult['success'] ? ($approvalResult['data']['status'] ?? 'submitted') : 'approval_failed', 'approval_error' => $approvalResult['success'] ? null : $approvalResult['message'], ]); } /** * Create multiple templates in batch. * * @param array $templates Array of template definitions * @return array Results for each template */ public function createDefaultTemplatesBatch(array $templates): array { $results = []; foreach ($templates as $key => $template) { $results[$key] = $this->createAndSubmitTemplate($template); } $successCount = count(array_filter($results, fn($r) => $r['success'])); $failCount = count($results) - $successCount; return $this->successResponse( sprintf(__eva('%d templates created, %d failed'), $successCount, $failCount), [ 'results' => $results, 'success_count' => $successCount, 'fail_count' => $failCount, 'total' => count($results), ] ); } }