Files
marco 38295e7e88 feat(cms): implement complete SysSiteCMS content management system
Initial implementation of the HomeSysPanel CMS built with Laravel 13.0.
Includes complete admin panel, content management, user authentication,
modular architecture for products/services/chatbot, and marketing tools.

Key features:
- User authentication with email verification and password reset
- Role-based access control (Admin/User with granular permissions)
- Content management (pages, news, FAQ, downloads, galleries, banners)
- Contact management system with subjects and responses
- Marketing tools (email campaigns, visitor analytics, social sharing)
- Modular architecture (Products, Services, Chatbot modules)
- AI Chatbot integration with OpenRouter.ai and LlamaCpp support
- Visitor tracking with geolocation
- SEO optimization with meta tags and Open Graph
- Responsive design with Bootstrap 5
- Dark mode support
- Maintenance mode
- Feature request system
2026-09-07 19:06:53 -03:00

169 lines
7.3 KiB
PHP
Executable File

<?php
namespace App\Modules\Chatbot\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use App\Models\Setting;
use App\Modules\Chatbot\Models\ChatbotConversation;
use App\Modules\Chatbot\Models\ChatbotKnowledge;
use App\Modules\Chatbot\Models\ChatbotRule;
class OpenRouterService
{
/**
* Send a chat message to OpenRouter API and get a response.
*
* @param array $messages Array of messages with 'role' and 'content'
* @return array ['content' => string, 'tokens_used' => int|null]
*/
public function chat(array $messages, ?ChatbotConversation $conversation = null): array
{
$apiKey = Setting::get('chatbot_openrouter_api_key');
$model = Setting::get('chatbot_openrouter_model', 'openai/gpt-3.5-turbo');
if (empty($apiKey)) {
Log::warning('Chatbot: OpenRouter API key not configured.');
return [
'content' => 'Desculpe, o assistente virtual não está configurado corretamente. Por favor, entre em contato com a empresa.',
'tokens_used' => null,
];
}
$systemPrompt = $this->buildSystemPrompt($conversation);
$payload = [
'model' => $model,
'messages' => array_merge(
[['role' => 'system', 'content' => $systemPrompt]],
$messages
),
'max_tokens' => (int) Setting::get('chatbot_max_tokens', 500),
'temperature' => (float) Setting::get('chatbot_temperature', 0.7),
];
try {
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $apiKey,
'Content-Type' => 'application/json',
'HTTP-Referer' => config('app.url'),
'X-Title' => 'SysSiteCMS Chatbot',
])->post('https://openrouter.ai/api/v1/chat/completions', $payload);
if ($response->successful()) {
$data = $response->json();
$tokensUsed = $data['usage']['total_tokens'] ?? null;
// Update cumulative token counter
if ($tokensUsed) {
$currentTotal = (int) Setting::get('chatbot_total_tokens_used', 0);
Setting::set('chatbot_total_tokens_used', $currentTotal + $tokensUsed, 'chatbot');
}
return [
'content' => $data['choices'][0]['message']['content'] ?? 'Desculpe, não consegui processar sua mensagem.',
'tokens_used' => $tokensUsed,
];
}
Log::error('Chatbot: OpenRouter API error', [
'status' => $response->status(),
'body' => $response->body(),
]);
return [
'content' => 'Desculpe, estou com dificuldades para responder no momento. Tente novamente em instantes.',
'tokens_used' => null,
];
} catch (\Exception $e) {
Log::error('Chatbot: Exception calling OpenRouter API', [
'message' => $e->getMessage(),
]);
return [
'content' => 'Desculpe, ocorreu um erro de conexão. Tente novamente em instantes.',
'tokens_used' => null,
];
}
}
/**
* Build the system prompt with company knowledge.
*/
protected function buildSystemPrompt(?ChatbotConversation $conversation = null): string
{
$companyName = Setting::get('company_name', 'Nossa Empresa');
$companyEmail = Setting::get('company_email');
$companyPhone = Setting::get('company_phone');
$companyAddress = Setting::get('company_address');
$companyHours = Setting::get('company_hours');
$customInstructions = Setting::get('chatbot_custom_instructions', '');
$prompt = "Você é um assistente virtual de atendimento ao cliente da empresa **{$companyName}**.\n\n";
$prompt .= "## Regras Importantes:\n";
$prompt .= "- Responda APENAS sobre a empresa {$companyName}, seus produtos, serviços e informações relacionadas.\n";
$prompt .= "- Seja sempre educado, profissional e prestativo.\n";
$prompt .= "- Responda em português do Brasil.\n";
$prompt .= "- Se não souber a resposta, diga que não possui essa informação e sugira entrar em contato pelo telefone ou e-mail da empresa.\n";
$prompt .= "- NUNCA invente informações que não foram fornecidas na base de conhecimento.\n";
$prompt .= "- NUNCA forneça informações sobre concorrentes ou outras empresas.\n";
$prompt .= "- NUNCA recomende ou sugira que o visitante faça pesquisas na internet (ex: \"pesquise no Google\", \"busque online\", \"procure no site\", \"acesse o link\", etc.). Toda informação deve ser fornecida diretamente por você com base exclusivamente no conteúdo da empresa {$companyName}.\n";
$prompt .= "- NUNCA forneça links, URLs ou endereços de sites externos, redes sociais ou buscadores. Utilize apenas os canais de contato oficiais da empresa (telefone, e-mail, WhatsApp ou endereço físico) listados abaixo.\n";
$prompt .= "- NUNCA diga frases como \"você pode pesquisar\", \"procure saber\", \"verifique na internet\", \"consulte online\" ou qualquer variação que incentive o visitante a buscar informação fora deste chat.\n";
$prompt .= "- Mantenha as respostas concisas e objetivas.\n\n";
if ($companyEmail) {
$prompt .= "- E-mail de contato: {$companyEmail}\n";
}
if ($companyPhone) {
$prompt .= "- Telefone de contato: {$companyPhone}\n";
}
if ($companyAddress) {
$prompt .= "- Endereço: {$companyAddress}\n";
}
if ($companyHours) {
$prompt .= "- Horário de funcionamento: {$companyHours}\n";
}
$prompt .= "\n";
// Add visitor info if available
if ($conversation && $conversation->visitor_name) {
$prompt .= "## Dados do Visitante:\n";
$prompt .= "- **Nome**: {$conversation->visitor_name}\n";
if ($conversation->visitor_phone) {
$prompt .= "- **Telefone**: {$conversation->visitor_phone}\n";
}
if ($conversation->visitor_city) {
$prompt .= "- **Cidade**: {$conversation->visitor_city}\n";
}
$prompt .= "\nSempre cumprimente o visitante pelo nome e personalize o atendimento.\n";
$prompt .= "\n";
}
// Add knowledge base entries
$knowledge = ChatbotKnowledge::getFormattedKnowledge();
if ($knowledge) {
$prompt .= "## Base de Conhecimento da Empresa:\n";
$prompt .= $knowledge;
}
// Add configurable AI rules (admin-defined behavior constraints)
$rules = ChatbotRule::getFormattedRules();
if ($rules) {
$prompt .= "\n## Regras Obrigatórias de Comportamento:\n";
$prompt .= "As regras abaixo foram definidas pela administração e DEVEM ser seguidas rigorosamente em TODAS as respostas:\n";
$prompt .= $rules . "\n";
}
// Add custom instructions
if ($customInstructions) {
$prompt .= "\n## Instruções Adicionais:\n{$customInstructions}\n";
}
return $prompt;
}
}