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));
}
}
