cambios importantes
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
class NormalBot
|
||||
{
|
||||
public static function process(array $company, array $context, string $input): ?array
|
||||
{
|
||||
$config = self::getConfig($company);
|
||||
$commands = $config['commands'] ?? [];
|
||||
$flows = $config['flows'] ?? [];
|
||||
$menus = $config['menus'] ?? [];
|
||||
|
||||
$ctxId = null;
|
||||
$botCtx = ConversationContext::getOrCreate((int)$company['id'], $context['from'], 'normal');
|
||||
$ctxId = (int)$botCtx['id'];
|
||||
$currentNode = $botCtx['current_node'];
|
||||
|
||||
$normalized = self::normalize($input);
|
||||
|
||||
if ($currentNode !== null && isset($flows[$currentNode])) {
|
||||
return self::handleFlow($flows[$currentNode], $context, $company, $ctxId);
|
||||
}
|
||||
|
||||
$matchedCommand = null;
|
||||
foreach ($commands as $keyword => $action) {
|
||||
if ($normalized === self::normalize($keyword)) {
|
||||
$matchedCommand = $action;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($matchedCommand !== null) {
|
||||
ConversationContext::updateNode($ctxId, $matchedCommand);
|
||||
|
||||
if (isset($flows[$matchedCommand])) {
|
||||
return self::handleFlow($flows[$matchedCommand], $context, $company, $ctxId);
|
||||
}
|
||||
|
||||
if (isset($menus[$matchedCommand])) {
|
||||
return self::buildMenuResponse($menus[$matchedCommand], $context['from'], $company);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$greeting = $config['greeting'] ?? null;
|
||||
if ($greeting !== null && $currentNode === null) {
|
||||
ConversationContext::updateNode($ctxId, 'greeting');
|
||||
|
||||
if (isset($flows['greeting'])) {
|
||||
return self::handleFlow($flows['greeting'], $context, $company, $ctxId);
|
||||
}
|
||||
|
||||
return self::sendText($greeting, $context['from'], $company);
|
||||
}
|
||||
|
||||
$fallback = $config['fallback'] ?? null;
|
||||
if ($fallback !== null) {
|
||||
return self::sendText($fallback, $context['from'], $company);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function handleFlow(array $flow, array $context, array $company, int $ctxId): ?array
|
||||
{
|
||||
$type = $flow['type'] ?? 'text';
|
||||
|
||||
return match ($type) {
|
||||
'text' => self::sendText($flow['message'] ?? '', $context['from'], $company),
|
||||
'image' => self::sendImage($flow['media_id'] ?? '', $flow['caption'] ?? null, $context['from'], $company),
|
||||
'menu' => self::buildMenuResponse($flow['menu'] ?? [], $context['from'], $company),
|
||||
'function' => self::executeFunction($flow['function'] ?? '', $flow['params'] ?? [], $context, $company, $ctxId),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
private static function executeFunction(string $function, array $params, array $context, array $company, int $ctxId): ?array
|
||||
{
|
||||
return match ($function) {
|
||||
'reset' => (function () use ($ctxId, $context, $company) {
|
||||
ConversationContext::reset($ctxId);
|
||||
return self::sendText('¿En qué más puedo ayudarte?', $context['from'], $company);
|
||||
})(),
|
||||
'forward_to_ai' => null,
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
private static function buildMenuResponse(array $menu, string $to, array $company): array
|
||||
{
|
||||
$menuType = $menu['type'] ?? 'list';
|
||||
|
||||
if ($menuType === 'list') {
|
||||
$interactive = [
|
||||
'type' => 'list',
|
||||
'header' => [
|
||||
'type' => 'text',
|
||||
'text' => mb_substr($menu['header'] ?? 'Menú', 0, 60),
|
||||
],
|
||||
'body' => [
|
||||
'text' => mb_substr($menu['body'] ?? 'Selecciona una opción:', 0, 1024),
|
||||
],
|
||||
'footer' => [
|
||||
'text' => mb_substr($menu['footer'] ?? $company['display_name'] ?? '', 0, 60),
|
||||
],
|
||||
'action' => [
|
||||
'button' => mb_substr($menu['button'] ?? 'Ver opciones', 0, 20),
|
||||
'sections' => [],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($menu['sections'] ?? [] as $section) {
|
||||
$rows = [];
|
||||
foreach ($section['rows'] ?? [] as $row) {
|
||||
$rows[] = [
|
||||
'id' => mb_substr($row['id'] ?? '', 0, 200),
|
||||
'title' => mb_substr($row['title'] ?? '', 0, 24),
|
||||
'description' => isset($row['description']) ? mb_substr($row['description'], 0, 72) : null,
|
||||
];
|
||||
}
|
||||
$interactive['action']['sections'][] = [
|
||||
'title' => mb_substr($section['title'] ?? '', 0, 24),
|
||||
'rows' => $rows,
|
||||
];
|
||||
}
|
||||
|
||||
return self::enqueueInteractive($to, $interactive, $company);
|
||||
}
|
||||
|
||||
if ($menuType === 'buttons') {
|
||||
$buttons = [];
|
||||
foreach ($menu['buttons'] ?? [] as $btn) {
|
||||
$buttons[] = [
|
||||
'type' => 'reply',
|
||||
'reply' => [
|
||||
'id' => mb_substr($btn['id'] ?? '', 0, 256),
|
||||
'title' => mb_substr($btn['title'] ?? '', 0, 20),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$interactive = [
|
||||
'type' => 'button',
|
||||
'body' => [
|
||||
'text' => mb_substr($menu['body'] ?? 'Selecciona:', 0, 1024),
|
||||
],
|
||||
'action' => ['buttons' => $buttons],
|
||||
];
|
||||
|
||||
return self::enqueueInteractive($to, $interactive, $company);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function sendText(string $text, string $to, array $company): array
|
||||
{
|
||||
return [
|
||||
'action' => 'send',
|
||||
'type' => 'text',
|
||||
'to' => $to,
|
||||
'payload' => json_encode(['text' => $text]),
|
||||
];
|
||||
}
|
||||
|
||||
private static function sendImage(string $mediaId, ?string $caption, string $to, array $company): array
|
||||
{
|
||||
return [
|
||||
'action' => 'send',
|
||||
'type' => 'image',
|
||||
'to' => $to,
|
||||
'payload' => json_encode(['media_id' => $mediaId, 'caption' => $caption]),
|
||||
];
|
||||
}
|
||||
|
||||
private static function enqueueInteractive(string $to, array $interactive, array $company): array
|
||||
{
|
||||
return [
|
||||
'action' => 'send',
|
||||
'type' => 'interactive',
|
||||
'to' => $to,
|
||||
'payload' => json_encode(['interactive' => $interactive]),
|
||||
];
|
||||
}
|
||||
|
||||
private static function getConfig(array $company): array
|
||||
{
|
||||
$json = $company['config_json'] ?? '';
|
||||
if ($json === '') {
|
||||
return [];
|
||||
}
|
||||
$config = json_decode($json, true);
|
||||
return is_array($config) ? $config : [];
|
||||
}
|
||||
|
||||
private static function normalize(string $input): string
|
||||
{
|
||||
$input = mb_strtolower(trim($input));
|
||||
$input = str_replace(['á', 'é', 'í', 'ó', 'ú', 'ü', 'ñ'], ['a', 'e', 'i', 'o', 'u', 'u', 'n'], $input);
|
||||
return preg_replace('/[^a-z0-9\s]/', '', $input);
|
||||
}
|
||||
|
||||
public static function processInteractive(array $company, array $context, string $input): ?array
|
||||
{
|
||||
$config = self::getConfig($company);
|
||||
$flows = $config['flows'] ?? [];
|
||||
|
||||
$botCtx = ConversationContext::getOrCreate((int)$company['id'], $context['from'], 'normal');
|
||||
$ctxId = (int)$botCtx['id'];
|
||||
|
||||
foreach ($flows as $flowId => $flow) {
|
||||
if (($flow['type'] ?? '') === 'menu') {
|
||||
$menu = $flow['menu'] ?? [];
|
||||
foreach ($menu['sections'] ?? [] as $section) {
|
||||
foreach ($section['rows'] ?? [] as $row) {
|
||||
if (($row['id'] ?? '') === $input) {
|
||||
ConversationContext::updateNode($ctxId, $row['id']);
|
||||
if (isset($flows[$row['id']])) {
|
||||
return self::handleFlow($flows[$row['id']], $context, $company, $ctxId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($menu['buttons'] ?? [] as $btn) {
|
||||
if (($btn['id'] ?? '') === $input) {
|
||||
ConversationContext::updateNode($ctxId, $btn['id']);
|
||||
if (isset($flows[$btn['id']])) {
|
||||
return self::handleFlow($flows[$btn['id']], $context, $company, $ctxId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user