dbb-1.0.x-dev/src/Controller/NodeRedirectController.php

src/Controller/NodeRedirectController.php
<?php

namespace Drupal\dbb\Controller;

use Drupal\Core\Entity\EntityInterface;
use Drupal\node\Controller\NodeViewController;
use pimax\FbBotApp;
use pimax\Messages\Message;
use pimax\Messages\MessageButton;
use pimax\Messages\MessageElement;
use pimax\Messages\QuickReplyButton;
use pimax\Messages\StructuredMessage;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;

/**
 * Custom node redirect controller.
 */
class NodeRedirectController extends NodeViewController
{
  /**
   * Request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  private $requestStack;

  /**
   * A logger instance.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected $logger;

  /**
   * Route Match to get Node from.
   *
   * @var \Drupal\Core\Routing\CurrentRouteMatch
   */
  protected $currentRouteMatch;

  /**
   * The paragraph storage.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $phStorage;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    $instance = parent::create($container);
    $instance->requestStack = $container->get('request_stack');
    $instance->logger = $container->get('logger.factory')->get('dbb');
    $instance->currentRouteMatch = $container->get('current_route_match');
    $instance->phStorage = $container->get('entity_type.manager')->getStorage('paragraph');
    return $instance;
  }

  /**
   * Veiw node cnonical handler.
   *
   * @param \Drupal\Core\Entity\EntityInterface $node
   *   Node content type.
   * @param string $view_mode
   *   View mode passed to route handler.
   * @param string $langcode
   *   Language code passed to route handler.
   *
   * @return \Symfony\Component\HttpFoundation\Response
   *   Return void.
   */
  public function view(EntityInterface $node, $view_mode = 'full', $langcode = null) {
    /** @var \Drupal\node\NodeInterface $node */
    // Redirect to the edit path on the chatbot_webhook content type.
    if ($node->getType() === 'chatbot_webhook') {
      $request = $this->requestStack->getCurrentRequest();
      // Log each get request if hub_mode parameter is not empty.
      if (!empty($request->query->get('hub_mode')) &&
        $hub_verify_token = $request->query->get('hub_verify_token')) {
        $this->logger->notice('Request from facebook, hub_mode: @hub_mode, hub_verify_token=> @hvt, hub_challenge=> @hc', [
          '@hub_mode' => $request->query->get('hub_mode'),
          '@hvt' => $hub_verify_token,
        ]);
        // 'hub_verify_token';
        if ($hub_verify_token === $node->field_fb_verify_token->value) {
          echo($request->query->get('hub_challenge'));
          return new Response('', 200);
        }
      }
      if ($input = json_decode(file_get_contents('php://input'), true)) {
        // Handle delivery notifications.
        if (!empty($input['entry'][0]['messaging'][0]['delivery'])) {
          $this->logger->notice('Delivery confirmation, post_input=> @input', [
            '@input' => json_encode($input),
          ]);
          return new Response('', 200);
        }
        // Handle read notifications.
        if (!empty($input['entry'][0]['messaging'][0]['read'])) {
          $this->logger->notice('Read confirmation, post_input=> @input', [
            '@input' => json_encode($input),
          ]);
          return new Response('', 200);
        }
        // Handle message echo.
        if (isset($input['entry'][0]['messaging'][0]['message']['is_echo']) &&
          $input['entry'][0]['messaging'][0]['message']['is_echo']) {
          $this->logger->notice('TODO: Echo message, post_input=> @input', [
            '@input' => json_encode($input),
          ]);
          return new Response('', 200);
        }
        // Handle undefined Facebook request to webhook.
        if (!isset($input['entry'][0]['messaging'])) {
          $this->logger->notice('TODO: Handle undefined fb request, post_input=> @input', [
            '@input' => json_encode($input),
          ]);
          return new Response('', 200);
        }

        // Handle messaging.
        $this->logger->notice('Request from facebook, post_input=> @input', [
          '@input' => json_encode($input),
        ]);

        $payload = NULL;
        $sender = $input['entry'][0]['messaging'][0]['sender']['id'];
        if (isset($input['entry'][0]['messaging'][0]['message'])) {
          $message = $input['entry'][0]['messaging'][0]['message']['text'];
        } elseif (isset($input['entry'][0]['messaging'][0]['postback'])) {
          $payload = $input['entry'][0]['messaging'][0]['postback']['payload'];
          $message = 'payload-> ' . $payload . ' | ' . $input['entry'][0]['messaging'][0]['postback']['title'];
        }

        $this->sender = $sender;
        $this->token = $node->field_fb_page_access_token->value;

        $this->logger->notice('Request from facebook, sender=>@sender, message=> @msg', [
          '@sender' => $sender,
          '@msg' => $message,
        ]);

        // Handle payload from paragraph's id or from specific payoad from facebook or menu.
        if ($payload) {
          // Handle payload postback_welcome it is payload for new visitors whom press GetStarted button.
          if ($payload == 'postback_welcome') {
            // Do nothing for now, cause we have implemented default mesasge in facebook to be sent to user
            $this->logger->notice('"postback_welcome" implement handler later.', [
              '@input' => json_encode($input),
            ]);
            return new Response('', 200);
          }

          // Handle start button from old menu
          if ($payload == 'start') {
            // Do nothing for now, cause we have implemented default mesasge in facebook to be sent to user
            $this->logger->notice('"start" implement handler later.', [
              '@input' => json_encode($input),
            ]);
            return new Response('', 200);
          }
          // Handle payload from paragraph's id.
          $this->handlePayload($payload);
          return new Response('', 200);
        }

        // Add handle multiple cardinality of field_fb_app_webhook so in ui we can add multiple steps.
        foreach ($node->field_fb_app_webhook as $step_entity) {
          $this->handlePayload($step_entity->target_id);
        }
        // Finalise response. No more interaction with fb api is needed.
        return new Response('', 200);
      }
      // Prevent visibility if not author.
      return new RedirectResponse('/node/' . $node->id() . '/edit');
    }
    // Otherwise, fall back to the parent route controller.
    else {
      return parent::view($node, $view_mode, $langcode);
    }
  }

  /**
   * Handler for payload - Step paragraph target_id.
   *
   * @param string|int $pl
   *   Payload of step paragraph.
   * @return void
   *   Result of handler performing.
   */
  private function handlePayload($pl) {
    /** @var \Drupal\paragraphs\ParagraphInterface $step */
    $step = $this->phStorage->load($pl);
    if (!$step) {
      $this->logger->notice('Step paragraph with id=> @id not found.', [
        '@id' => $pl,
      ]);
      return;
    }
    // Added handle multiple field_step values
    /** @var \Drupal\Core\Field\EntityReferenceFieldItemList $steps */
    $steps = $step->get('field_step');
    foreach ($steps->referencedEntities() as $delta => $step_entity) {
      // $this->logger->notice('> @count $step->field_step->first()->entity->getType()=> @type', [
      //   '@type' => $step->field_step->first()->entity->getType(),
      //   '@count' => $step->field_step->count()
      // ]);
      /** @var \Drupal\paragraphs\ParagraphInterface $step_entity */
      switch ($step_entity->getType()) {
        case 'text':
          $message_to_reply = $step_entity->field_text->value;
          $this->sendText($message_to_reply);
          continue 2;
        case 'structured_message':
          $flow_id = $step_entity->field_flow->first()->target_id;
          $this->handleFlow($flow_id);
          continue 2;
        default:
          # code...
          continue 2;
      }
    }
    return;
  }

  /**
   * FB messenger API Send answer for structured message.
   *
   * @param [type] $flow_id
   * @return void
   */
  private function handleFlow($flow_id) {
    /** @var \Drupal\paragraphs\ParagraphInterface $flow */
    $flow = $this->phStorage->load($flow_id);
    switch ($flow->getType()) {
      case 'message_buttons':
        $btn_text = $flow->field_msg_btn_text->value;
        $buttons = $flow->field_msg_btn_buttons->getValue();
        $this->sendButtons($btn_text, $buttons);
        break;
      case 'message_generic':
        $generic_elements = $flow->field_msg_gen_elements->getValue();
        $this->handleGenericMessage($generic_elements);
        break;
      default:
        # code...
        break;
    }
    return;
  }

  /**
   * FB messenger API Send answer for message type Generic.
   * @param array $generic_elements
   *   Array of referenced generic elements target_id
   * @return void
   */
  private function handleGenericMessage($generic_elements) {
    $elements = [];
    foreach($generic_elements as $generic_element) {
      /** @var \Drupal\paragraphs\ParagraphInterface $element */
      $element = $this->phStorage->load($generic_element['target_id']);
      switch ($element->getType()) {
        case 'generic_element':
          $title = $element->field_gen_elem_title->value;
          $subtitle = $element->field_gen_elem_subtitle->value;
          // TODO: Add support for empty media field: field_gen_elem_img
          $image_uri = $element->field_gen_elem_img->entity->field_media_image->entity->getFileUri();
          $image_url = \Drupal::service('file_url_generator')->generateAbsoluteString($image_uri);
          $buttons = $element->field_gen_elem_btns->getValue();
          $elements[] = new MessageElement(
            $title,
            $subtitle,
            $image_url,
            $this->getButtonsArray($buttons)
          );
          break;
        default:
          # code...
          break;
      }
    }

    $bot = new FbBotApp($this->token);
    $bot->send(new StructuredMessage(
      $this->sender,
      StructuredMessage::TYPE_GENERIC,
      [
        'elements' => $elements,
      ]
    ));
  }

  /**
   * FB messenger API Send Button answer.
   *
   * @param [type]|null $btn_text
   *   Text above buttons.
   * @param array $buttons
   *   Array of referenced buttons target_id
   * @return void
   */
  private function sendButtons($btn_text, $buttons) {
    // HOWTO send answer with Drupal\dbb library?
    // - Make bot instance
    // - make sure use statement are exists.
    // @see https://github.com/pimax/fb-messenger-php-example/blob/master/index.php
    $bot = new FbBotApp($this->token);

    $buttons_array = $this->getButtonsArray($buttons);
    $bot->send(new StructuredMessage(
      $this->sender,
      StructuredMessage::TYPE_BUTTON,
      [
        'text' => $btn_text,
        'buttons' => $buttons_array,
      ]
    ));
  }

  /**
   * Get buttons array from referenced buttons.
   */
  private function getButtonsArray($buttons) {
    $buttons_array = [];
    foreach ($buttons as $button) {
      switch ($this->phStorage->load($button['target_id'])->field_btn_exec->entity->getType()) {
        case 'button_link':
          $buttons_array[] = new MessageButton(
            MessageButton::TYPE_WEB,
            $this->phStorage->load($button['target_id'])->field_btn_caption->value,
            $this->phStorage->load($button['target_id'])->field_btn_exec->entity->field_url->uri
          );
          break;
          //TODO: Add support for button_call TYPE_CALL
        default:
          $buttons_array[] = new MessageButton(
            MessageButton::TYPE_POSTBACK,
            $this->phStorage->load($button['target_id'])->field_btn_caption->value,
            $this->phStorage->load($button['target_id'])->field_btn_exec->target_id
          );
          break;
      }
    }
    return $buttons_array;
  }

  /**
   * FB messenger API Send text answer.
   *
   * @param string $message_to_reply
   * @return void
   */
  private function sendText($message_to_reply) {
    $sender = $this->sender;
    $access_token = $this->token;
    // Use FbBotApp library instead of curl
    $bot = new FbBotApp($this->token);
    $bot->send(new Message($this->sender, $message_to_reply));
  }
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc