diff --git a/Magento2/app/code/Werules/Chatbot/Block/Adminhtml/System/Config/Form/Field/DefaultReplies.php b/Magento2/app/code/Werules/Chatbot/Block/Adminhtml/System/Config/Form/Field/DefaultReplies.php new file mode 100644 index 0000000..0c58c49 --- /dev/null +++ b/Magento2/app/code/Werules/Chatbot/Block/Adminhtml/System/Config/Form/Field/DefaultReplies.php @@ -0,0 +1,135 @@ +. + */ + +namespace Werules\Chatbot\Block\Adminhtml\System\Config\Form\Field; + +class DefaultReplies extends \Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray +{ + /** + * @var \Magento\Framework\Data\Form\Element\Factory + */ + protected $_elementFactory; + protected $_itemRendererMatchCase; + protected $_itemRendererMatchMode; + protected $_itemRendererEnable; + + /** + * @param \Magento\Backend\Block\Template\Context $context + * @param \Magento\Framework\Data\Form\Element\Factory $elementFactory + * @param array $data + */ + public function __construct( + \Magento\Backend\Block\Template\Context $context, + \Magento\Framework\Data\Form\Element\Factory $elementFactory, + array $data = array() + ) + { + $this->_elementFactory = $elementFactory; + parent::__construct($context,$data); + } + protected function _construct() + { + $this->addColumn('enable_reply', array( + 'label' => __("Enable"), + 'renderer' => $this->_getRendererEnable() + )); +// $this->addColumn('stop_processing', array( +// 'label' => __("Stop Processing"), +// 'renderer' => $this->_getRendererEnable() +// )); + $this->addColumn('match_mode', array( + 'label' => __("Match Mode"), + 'renderer' => $this->_getRendererMatchMode() + )); + $this->addColumn('match_sintax', array( + 'label' => __("Match Text or Regex"), + //'style' => 'width: 100%', + 'class' => 'validate-no-html-tags' + )); + $this->addColumn('match_case', array( + 'label' => __("Match Case"), + 'renderer' => $this->_getRendererMatchCase() + )); + $this->addColumn('reply_text', array( + 'label' => __("Reply Text"), + //'style' => 'width: 100%', + 'class' => 'validate-no-html-tags' + )); + + $this->_addAfter = false; + $this->_addButtonLabel = __("Add"); + parent::_construct(); + } + + protected function _getRendererYesNo() + { + return $this->getLayout()->createBlock( + 'Werules\Chatbot\Block\Adminhtml\System\Config\Options\YesNo', + '', + array('data' => array('is_render_to_js_template' => true)) +// array('is_render_to_js_template' => true) + ); // ->setExtraParams('style="width: 100%;"'); + } + + protected function _getRendererEnable() + { + if (!$this->_itemRendererEnable) + { + $this->_itemRendererEnable = $this->_getRendererYesNo(); + } + return $this->_itemRendererEnable; + } + + protected function _getRendererMatchCase() + { + if (!$this->_itemRendererMatchCase) + { + $this->_itemRendererMatchCase = $this->_getRendererYesNo(); + } + return $this->_itemRendererMatchCase; + } + + protected function _getRendererMatchMode() + { + if (!$this->_itemRendererMatchMode) + { + $this->_itemRendererMatchMode = $this->getLayout()->createBlock( + 'Werules\Chatbot\Block\Adminhtml\System\Config\Form\Field\MatchModeSelect', + '', + array('data' => array('is_render_to_js_template' => true)) +// array('is_render_to_js_template' => true) + ); // ->setExtraParams('style="width: 100%;"'); + } + return $this->_itemRendererMatchMode; + } + + protected function _prepareArrayRow(\Magento\Framework\DataObject $row) + { + $optionExtraAttr = array(); + $optionExtraAttr['option_' . $this->_getRendererEnable()->calcOptionHash($row->getData('enable_reply'))] = 'selected="selected"'; + $optionExtraAttr['option_' . $this->_getRendererMatchMode()->calcOptionHash($row->getData('match_mode'))] = 'selected="selected"'; + $optionExtraAttr['option_' . $this->_getRendererMatchCase()->calcOptionHash($row->getData('match_case'))] = 'selected="selected"'; + + $row->setData( + 'option_extra_attrs', $optionExtraAttr + ); + } +} \ No newline at end of file diff --git a/Magento2/app/code/Werules/Chatbot/Block/Adminhtml/System/Config/Form/Field/MatchModeList.php b/Magento2/app/code/Werules/Chatbot/Block/Adminhtml/System/Config/Form/Field/MatchModeList.php new file mode 100644 index 0000000..670dcda --- /dev/null +++ b/Magento2/app/code/Werules/Chatbot/Block/Adminhtml/System/Config/Form/Field/MatchModeList.php @@ -0,0 +1,41 @@ +. + */ + +namespace Werules\Chatbot\Block\Adminhtml\System\Config\Form\Field; + +class MatchModeList implements \Magento\Framework\Option\ArrayInterface +{ + /** + * Provide available options as a value/label array + * + * @return array + */ + public function toOptionArray() + { + return array( + array('value' => 0, 'label' => __("Equals to")), + array('value' => 1, 'label' => __("Starts With")), + array('value' => 2, 'label' => __("Ends With")), + array('value' => 3, 'label' => __("Contains")), + array('value' => 4, 'label' => __("Match RegEx")) + ); + } +} \ No newline at end of file diff --git a/Magento2/app/code/Werules/Chatbot/Block/Adminhtml/System/Config/Form/Field/MatchModeSelect.php b/Magento2/app/code/Werules/Chatbot/Block/Adminhtml/System/Config/Form/Field/MatchModeSelect.php new file mode 100644 index 0000000..85aa524 --- /dev/null +++ b/Magento2/app/code/Werules/Chatbot/Block/Adminhtml/System/Config/Form/Field/MatchModeSelect.php @@ -0,0 +1,53 @@ +. + */ + +namespace Werules\Chatbot\Block\Adminhtml\System\Config\Form\Field; + +class MatchModeSelect extends \Magento\Framework\View\Element\Html\Select +{ + /** + * @var \Werules\Chatbot\Block\Adminhtml\System\Config\Form\Field\MatchModeList + */ + protected $_matchModeList; + + public function __construct( + \Werules\Chatbot\Block\Adminhtml\System\Config\Form\Field\MatchModeList $MatchModeList, + \Magento\Backend\Block\Template\Context $context, array $data = array()) + { + parent::__construct($context, $data); + $this->_matchModeList = $MatchModeList; + } + + public function _toHtml() + { + if (!$this->getOptions()) { + foreach ($this->_matchModeList->toOptionArray() as $option) { + $this->addOption($option['value'], $option['label']); + } + } + return parent::_toHtml(); + } + + public function getName() + { + return $this->getInputName(); + } +} \ No newline at end of file diff --git a/Magento2/app/code/Werules/Chatbot/Block/Adminhtml/System/Config/Form/Field/WelcomeOptions.php b/Magento2/app/code/Werules/Chatbot/Block/Adminhtml/System/Config/Form/Field/WelcomeOptions.php new file mode 100644 index 0000000..f15b05b --- /dev/null +++ b/Magento2/app/code/Werules/Chatbot/Block/Adminhtml/System/Config/Form/Field/WelcomeOptions.php @@ -0,0 +1,91 @@ +. + */ + +namespace Werules\Chatbot\Block\Adminhtml\System\Config\Form\Field; + +class WelcomeOptions extends \Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray +{ + /** + * @var \Magento\Framework\Data\Form\Element\Factory + */ + protected $_elementFactory; + protected $_itemRendererEnable; + + /** + * @param \Magento\Backend\Block\Template\Context $context + * @param \Magento\Framework\Data\Form\Element\Factory $elementFactory + * @param array $data + */ + public function __construct( + \Magento\Backend\Block\Template\Context $context, + \Magento\Framework\Data\Form\Element\Factory $elementFactory, + array $data = array() + ) + { + $this->_elementFactory = $elementFactory; + parent::__construct($context,$data); + } + protected function _construct() + { + $this->addColumn('enable_option', array( + 'label' => __("Enable"), + 'renderer' => $this->_getRendererEnable() + )); + $this->addColumn('option_text', array( + 'label' => __("Option Text"), + //'style' => 'width: 100%', + 'class' => 'validate-no-html-tags' + )); + + $this->_addAfter = false; + $this->_addButtonLabel = __("Add"); + parent::_construct(); + } + + protected function _getRendererYesNo() + { + return $this->getLayout()->createBlock( + 'Werules\Chatbot\Block\Adminhtml\System\Config\Options\YesNo', + '', + array('data' => array('is_render_to_js_template' => true)) +// array('is_render_to_js_template' => true) + ); // ->setExtraParams('style="width: 100%;"'); + } + + protected function _getRendererEnable() + { + if (!$this->_itemRendererEnable) + { + $this->_itemRendererEnable = $this->_getRendererYesNo(); + } + return $this->_itemRendererEnable; + } + + protected function _prepareArrayRow(\Magento\Framework\DataObject $row) + { + $optionExtraAttr = array(); + $optionExtraAttr['option_' . $this->_getRendererEnable()->calcOptionHash($row->getData('enable_option'))] = 'selected="selected"'; + + $row->setData( + 'option_extra_attrs', $optionExtraAttr + ); + } +} \ No newline at end of file diff --git a/Magento2/app/code/Werules/Chatbot/Helper/Data.php b/Magento2/app/code/Werules/Chatbot/Helper/Data.php index 909d031..f1dfb53 100644 --- a/Magento2/app/code/Werules/Chatbot/Helper/Data.php +++ b/Magento2/app/code/Werules/Chatbot/Helper/Data.php @@ -121,6 +121,24 @@ public function logger($text, $file = 'werules_chatbot.log') // TODO find a bett $logger->info(var_export($text, true)); } + public function startsWith($haystack, $needle) + { + $length = strlen($needle); + if ($length == 0) + return true; + + return substr($haystack, 0, $length) === $needle; + } + + public function endsWith($haystack, $needle) + { + $length = strlen($needle); + if ($length == 0) + return true; + + return (substr($haystack, -$length) === $needle); + } + public function excerpt($text, $size) { if (strlen($text) > $size) @@ -234,7 +252,13 @@ public function processIncomingMessage($message) $chatbotAPI = $this->createChatbotAPI($chatbotAPI, $message); $welcomeMessage = $this->getWelcomeMessage($message); if ($welcomeMessage) + { + if ($message->getStatus() != $this->_define::PROCESSED) + $message->updateIncomingMessageStatus($this->_define::PROCESSED); + array_push($result, $welcomeMessage); + return $result; + } } $enabled = $this->getConfigValue($this->_configPrefix . '/general/enable'); @@ -317,6 +341,8 @@ public function processOutgoingMessage($outgoingMessage) $result = $chatbotAPI->sendReceiptList($outgoingMessage); else if ($outgoingMessage->getContentType() == $this->_define::TEXT_WITH_OPTIONS) // LIST_WITH_IMAGE $result = $chatbotAPI->sendMessageWithOptions($outgoingMessage); + else if ($outgoingMessage->getContentType() == $this->_define::NO_REPLY_MESSAGE) + $result = true; if ($result) { @@ -350,7 +376,8 @@ public function processOutgoingMessage($outgoingMessage) else $lastCommandParameter = null; - $chatbotAPI->setChatbotAPILastCommandDetails($lastCommandText, $lastListedQuantity, $lastConversationState, $lastCommandParameter); + if ($currentCommandDetails) + $chatbotAPI->setChatbotAPILastCommandDetails($lastCommandText, $lastListedQuantity, $lastConversationState, $lastCommandParameter); } if ($outgoingMessage->getStatus() != $this->_define::PROCESSED) @@ -373,6 +400,7 @@ private function processMessageRequest($message) $errorMessages = array(); $conversationStateResponses = array(); $payloadCommandResponses = array(); + $defaultRepliesResponses = array(); $NLPResponses = array(); $this->setHelperMessageAttributes($message); @@ -411,6 +439,20 @@ private function processMessageRequest($message) } } + $enableDefaultReplies = $this->getConfigValue($this->_configPrefix . '/general/enable_default_replies'); + if ($enableDefaultReplies == $this->_define::ENABLED) + { + if (count($responseContent) <= 0) + $defaultRepliesResponses = $this->handleDefaultReplies($message); + if ($defaultRepliesResponses) + { + foreach ($defaultRepliesResponses as $defaultReplyResponse) + { + array_push($responseContent, $defaultReplyResponse); + } + } + } + if (count($responseContent) <= 0) $commandResponses = $this->handleCommands($message); if ($commandResponses) @@ -512,7 +554,7 @@ private function handleNaturalLanguageProcessor($message) { if (isset($intent['confidence'])) { - $entityData = $this->getCommandNLPEntityData($commandCode); + $entityData = $this->getCommandNLPEntityData($commandCode); // get all NPL configs from backend if (isset($entityData['confidence'])) { $confidence = $entityData['confidence']; @@ -605,6 +647,10 @@ private function handleConversationState($content, $senderId, $keyword = false) { $result = $this->listOrderDetailsFromOrderId($messageContent, $senderId); } + else if ($chatbotAPI->getConversationState() == $this->_define::CONVERSATION_SUPPORT) + { + $result = $this->getNoMessage(); + } // if ($result) // { @@ -854,7 +900,7 @@ private function listProductsFromCategory($messageContent, $messagePayload, $sen private function prepareCommandsList() { - if (isset($this->_commandsList) || isset($this->_completeCommandsList)) + if (isset($this->_commandsList) && isset($this->_completeCommandsList)) return true; $serializedCommands = $this->getConfigValue($this->_configPrefix . '/general/commands_list'); @@ -1050,6 +1096,74 @@ private function handleCommandsWithParameters($message, $keyword, $commandCode) return $result; } + private function handleDefaultReplies($message) + { + $result = array(); + $defaultReplies = $this->getDefaultRepliesData(); + $text = $message->getContent(); + + foreach ($defaultReplies as $defaultReply) + { + if (isset($defaultReply['enable_reply'])) + { + if ($defaultReply['enable_reply'] == $this->_define::ENABLED) + { + // MODES: +// EQUALS_TO +// STARTS_WITH +// ENDS_WITH +// CONTAINS +// MATCH_REGEX + + $matched = false; + $match = $defaultReply["match_sintax"]; + if ($defaultReply["match_case"] != $this->_define::ENABLED) + { + $match = strtolower($match); + $text = strtolower($text); + } + + if ($defaultReply['match_mode'] == $this->_define::EQUALS_TO) + { + if ($text == $match) + $matched = true; + } + else if ($defaultReply['match_mode'] == $this->_define::STARTS_WITH) + { + if ($this->startsWith($text, $match)) + $matched = true; + } + else if ($defaultReply['match_mode'] == $this->_define::ENDS_WITH) + { + + if ($this->endsWith($text, $match)) + $matched = true; + } + else if ($defaultReply['match_mode'] == $this->_define::CONTAINS) + { + if (strpos($text, $match) !== false) + $matched = true; + } + else if ($defaultReply['match_mode'] == $this->_define::MATCH_REGEX) + { + if (preg_match($match, $text)) + $matched = true; + } + + if ($matched) + { + $replyText = $defaultReply['reply_text']; + if ($replyText) + $result = $this->getTextMessageArray($replyText); + break; + } + } + } + } + + return $result; + } + private function handlePayloadCommands($message) { return $this->processPayloadCommands($message); @@ -1301,6 +1415,18 @@ private function setCurrentCommand($messageContent) } // GETS + private function getNoMessage() + { + $result = array(); + $responseMessage = array( + 'content_type' => $this->_define::NO_REPLY_MESSAGE, + 'content' => '', + 'current_command_details' => json_encode(array()) + ); + array_push($result, $responseMessage); + return $result; + } + private function getWelcomeMessage($message) { // $this->setHelperMessageAttributes($message); @@ -1308,8 +1434,43 @@ private function getWelcomeMessage($message) $text = $this->getConfigValue($this->_configPrefix . '/general/welcome_message'); if ($text != '') { - $contentObj = $this->getTextMessageArray($text); - $outgoingMessage = $this->createOutgoingMessage($message, reset($contentObj)); // TODO reset -> gets first item of array +// $contentObj = $this->getTextMessageArray($text); +// $outgoingMessage = $this->createOutgoingMessage($message, reset($contentObj)); // TODO reset -> gets first item of array + $enableMessageOptions = $this->getConfigValue($this->_configPrefix . '/general/enable_message_options'); + if ($enableMessageOptions == $this->_define::ENABLED) + { + $quickReplies = array(); + $welcomeMessageOptions = $this->getWelcomeMessageOptionsData(); + foreach ($welcomeMessageOptions as $optionId => $messageOption) + { + if (count($quickReplies) >= $this->_define::MAX_MESSAGE_ELEMENTS) + break; + + $quickReply = array( + 'content_type' => 'text', // TODO messenger pattern + 'title' => $messageOption['option_text'], + 'payload' => json_encode(array()) + ); + array_push($quickReplies, $quickReply); + } + + $contentObject = new \stdClass(); + $contentObject->message = $text; + $contentObject->quick_replies = $quickReplies; + $content = json_encode($contentObject); + $contentType = $this->_define::QUICK_REPLY; + } + else + { + $contentType = $this->_define::CONTENT_TEXT; + $content = $text; + } + $responseMessage = array( + 'content_type' => $contentType, + 'content' => $content, + 'current_command_details' => json_encode(array()) + ); + $outgoingMessage = $this->createOutgoingMessage($message, $responseMessage); // $this->processOutgoingMessage($outgoingMessage); } @@ -1908,6 +2069,26 @@ private function getCommandNLPEntityData($commandCode) return $result; } + private function getDefaultRepliesData() + { + $defaultReplies = array(); + $serializedDefaultReplies = $this->getConfigValue($this->_configPrefix . '/general/default_replies'); + if ($serializedDefaultReplies) + $defaultReplies = $this->_serializer->unserialize($serializedDefaultReplies); + + return $defaultReplies; + } + + private function getWelcomeMessageOptionsData() + { + $welcomeMessageOptions = array(); + $serializedWelcomeMessageOptions = $this->getConfigValue($this->_configPrefix . '/general/message_options'); + if ($serializedWelcomeMessageOptions) + $welcomeMessageOptions = $this->_serializer->unserialize($serializedWelcomeMessageOptions); + + return $welcomeMessageOptions; + } + private function getChatbotAPIModelBySenderId($senderId) { if (isset($this->_chatbotAPIModel)) @@ -2356,8 +2537,9 @@ private function processSupportCommand() $result = array(); $responseMessage = array( 'content_type' => $this->_define::CONTENT_TEXT, - 'content' => 'The SUPPORT command is still under development', // TODO + 'content' => __("You're on support mode. In a few words, please explain what would you like help with. To cancel send '%1'.", $this->getCommandText($this->_define::CANCEL_COMMAND_ID)), 'current_command_details' => json_encode(array( + 'conversation_state' => $this->_define::CONVERSATION_SUPPORT, 'command_text' => $this->getCommandText($this->_define::SUPPORT_COMMAND_ID) )) ); @@ -2405,9 +2587,42 @@ private function processHelpCommand() $text = $this->getConfigValue($this->_configPrefix . '/general/help_message'); if ($text) { + $listCommands = $this->getConfigValue($this->_configPrefix . '/general/enable_help_command_list'); + if ($listCommands == $this->_define::ENABLED) + { + $quickReplies = array(); + $enabledCommands = $this->getCommandsList(); + foreach ($enabledCommands as $commandId => $command) + { + if (count($quickReplies) >= $this->_define::MAX_MESSAGE_ELEMENTS) + break; + + $payload = array( + 'command' => $commandId, +// 'parameter' => '' + ); + $quickReply = array( + 'content_type' => 'text', // TODO messenger pattern + 'title' => $command['command_code'], + 'payload' => json_encode($payload) + ); + array_push($quickReplies, $quickReply); + } + + $contentObject = new \stdClass(); + $contentObject->message = $text; + $contentObject->quick_replies = $quickReplies; + $content = json_encode($contentObject); + $contentType = $this->_define::QUICK_REPLY; + } + else + { + $contentType = $this->_define::CONTENT_TEXT; + $content = $text; + } $responseMessage = array( - 'content_type' => $this->_define::CONTENT_TEXT, - 'content' => $text, + 'content_type' => $contentType, + 'content' => $content, 'current_command_details' => json_encode(array( 'command_text' => $this->getCommandText($this->_define::HELP_COMMAND_ID) )) diff --git a/Magento2/app/code/Werules/Chatbot/Helper/Define.php b/Magento2/app/code/Werules/Chatbot/Helper/Define.php index 91ac394..f605b8c 100644 --- a/Magento2/app/code/Werules/Chatbot/Helper/Define.php +++ b/Magento2/app/code/Werules/Chatbot/Helper/Define.php @@ -80,6 +80,7 @@ class Define const RECEIPT_LAYOUT = 4; const LIST_WITH_IMAGE = 5; const TEXT_WITH_OPTIONS = 6; + const NO_REPLY_MESSAGE = 7; // conversation states const CONVERSATION_STARTED = 0; @@ -87,6 +88,7 @@ class Define const CONVERSATION_SEARCH = 2; const CONVERSATION_EMAIL = 3; const CONVERSATION_TRACK_ORDER = 4; + const CONVERSATION_SUPPORT = 5; // API const MAX_MESSAGE_ELEMENTS = 7; @@ -99,4 +101,11 @@ class Define const DONT_CLEAR_MESSAGE_QUEUE = 0; const CLEAR_MESSAGE_QUEUE = 1; + + // DEFAULT REPLIES MODES + const EQUALS_TO = 0; + const STARTS_WITH = 1; + const ENDS_WITH = 2; + const CONTAINS = 3; + const MATCH_REGEX = 4; } \ No newline at end of file diff --git a/Magento2/app/code/Werules/Chatbot/Setup/UpgradeData.php b/Magento2/app/code/Werules/Chatbot/Setup/UpgradeData.php index f376e72..0dd61e3 100644 --- a/Magento2/app/code/Werules/Chatbot/Setup/UpgradeData.php +++ b/Magento2/app/code/Werules/Chatbot/Setup/UpgradeData.php @@ -36,7 +36,7 @@ public function upgrade( ModuleContextInterface $context ) { $setup->startSetup(); - if (version_compare($context->getVersion(), "1.0.3", "<")) { + if (version_compare($context->getVersion(), "1.0.4", "<")) { //Your upgrade script } $setup->endSetup(); diff --git a/Magento2/app/code/Werules/Chatbot/etc/adminhtml/system.xml b/Magento2/app/code/Werules/Chatbot/etc/adminhtml/system.xml index 0c6955d..32be612 100644 --- a/Magento2/app/code/Werules/Chatbot/etc/adminhtml/system.xml +++ b/Magento2/app/code/Werules/Chatbot/etc/adminhtml/system.xml @@ -11,7 +11,7 @@ Magento Chatbot v1.0.3 +

Magento Chatbot v1.0.4

To use this module you'll have to use SSL in your store.

]]>
@@ -67,7 +67,7 @@ Magento Chatbot v1.0.3 +

Magento Chatbot v1.0.4

To use this module you'll have to use SSL in your store.

]]>
@@ -126,54 +126,54 @@ First message the bot will send to your client. please check the maximum size for messages on Facebook API, otherwise your message might not be sent. you can use "{customername}" to send the customer name. - - - - - - - - - - - - - - - - - - - + + + + + + + Enable welcome message options. + Magento\Config\Model\Config\Source\Yesno + + + + + 1 + + + Welcome message options. + Magento\Config\Model\Config\Backend\Serialized\ArraySerialized + Werules\Chatbot\Block\Adminhtml\System\Config\Form\Field\WelcomeOptions + Message will be sent when customer asks for help. Please check the maximum size for messages on Facebook API, otherwise your message might not be sent. - - - - - + + + Enable command listing when customer ask for help. + Magento\Config\Model\Config\Source\Yesno + - Code of the commands and it's alias. Alias must be split by comma. eg.: "support,chat,talk,sac". + Code of the commands and it's alias. Alias must be split by comma. eg.: "support,chat,talk,sac". Magento\Config\Model\Config\Backend\Serialized\ArraySerialized Werules\Chatbot\Block\Adminhtml\System\Config\Form\Field\Commands - - - - - - - - - - - - - - + + + Enable default replies. + Magento\Config\Model\Config\Source\Yesno + + + + 1 + + + Replies to be send to the customer whenever matches one of the requirements. + Magento\Config\Model\Config\Backend\Serialized\ArraySerialized + Werules\Chatbot\Block\Adminhtml\System\Config\Form\Field\DefaultReplies + Enable Natural Language Processor replies. @@ -227,7 +227,7 @@ Magento Chatbot v1.0.3 +

Magento Chatbot v1.0.4

To use this module you'll have to use SSL in your store.

You should only change settings here if you know what you're doing.