Я создаю простого чат-бота через API Messenger Platform, и я в значительной степени застрял в том, как эффективно распознавать набор команд, на которые бот может реагировать. В настоящее время я использую оператор switch для обнаружения команд, начинающихся с восклицательного знака (например,! Showlist;! Additem <single/set of parameter(s)>
).
Вот что у меня сейчас есть:
switch(true){
case stristr($receivedMsg,'!additem'):
....
}
На любом этапе сопоставления код либо выполняет набор операторов, либо сначала экстраполирует возможные параметры, а затем выполняет с ними некоторые операторы.
Проблемы с вышеупомянутой установкой следующие:
в случае команд, которые принимают параметры, при извлечении этих параметров с помощью следующего оператора:
$item=str_replace('!additem', '', $receivedMsg);
очень легко включить нежелательный текст в параметры; вы можете иметь дело с пробелами с trim()
или подразумевать, что всегда будет пробел, и отредактируйте вышеприведенный оператор, чтобы включить его в функцию. Например.
$item=str_replace('!additem ', '', $receivedMsg);
но это создает другую проблему при попытке отделить команду от параметров.
Я знаю, что решением может быть жесткое кодирование с помощью систематических функций манипуляции со строками, но мне это не кажется правильным. Что люди делают в этой ситуации? Разве нет конкретного способа точно сопоставить команды и надежно определить возможные опечатки пользователей?
Вы не работали с регулярными выражениями в своем собственном решении, но пометили его правильно. От stristr()
Я обнаружил, что вы не ищете новых команд, поэтому применил ту же логику к RegEx:
$msg = 'Gonna add it !additem param1,param2';
preg_match('~(!\S+)\s*(.*)~', $msg, $match);
$command = $match[1];
$parameters = preg_split('~\s*,\s*~', $match[2]);
Я пытался сделать это в одну строку, но позже подумал, что это будет намного чище. Кстати, мне интересно, о заявлении переключателя.
Распределение RegEx:
~ # regex delimiter
( # Start of Capturing Group (1)
!\S+ # Match non-space characters that start with !
) # End of CG1
\s* # Any number of white-sapce characters
( # Start of CG2
.* # Match up to end
) # End of CG2
~ # regex delimiter
preg_split
тоже получает регулярное выражение в качестве первого аргумента и пытается разделить его, почти explode
с регулярным выражением \s*,\s*
означает запятую, которая может быть заключена в любое количество пробелов.
if ($receivedMsg[0] == '!')
switch (strtolower(substr(reset(explode(' ', $receivedMsg)), 1)))
{
case 'additem':
// do this
break;
case 'delitem':
// do that
break;
default:
echo 'Command not recognized.';
}
Ну, это один из способов сделать это. Вы также можете объявить массив с функциями, которые обрабатывают каждую команду, например:
$handles = [
'additem' = function ($args) { /* add something */ },
'delitem' = function ($args) { /* del something */ },
// ...
];
if ($receivedMsg[0] == '!')
{
$args = explode(' ', $receivedMsg);
$cmd = strtolower(substr($args[0], 1));
if (isset($handles[$cmd]))
$handles[$cmd]($args);
else
echo 'Command not recognized.';
}
Основываясь на моем решении на основе ответов, предоставленных @Havenhard и @revo, я написал следующее решение, которое идеально подходит для меня:
$this->senderId = $messaging['sender']['id'];
$command_handlers = [
'additem' => "addItemCommand",
'showlist' => "showListCommand",
'rngroup' => "renameGroupCommand"];
$actionCompletedOrh = new OutRequestHandler($this->senderId);
if(!empty($messaging['message']) && empty($messaging['message']['quick_reply'])){
$receivedMsg = $messaging['message']['text'];
$replyMsg = "";
$this->performSenderAction(0);
//isCommand uses this regex to perform the evaluation
//(^!\w+\s+([\w,\s]*$)|^!\w+$)"if($this->isCommand($receivedMsg)){
//regex matching to get params in raw form
preg_match("~(^!\w+\s+([\w,\s]*$)|^!\w+$)~",$receivedMsg,$match);
//regex matching to get the command
preg_match("~^!\w+~",$match[0],$_match);
$command = strtolower($_match[0]);
$params = null;
if(count($match)>2){
//the function below uses preg_split as in @revo's example
$params = $this->getCommandParams($match[2]);
}
if(array_key_exists(substr($command,1), $command_handlers)){
$func = $command_handlers[substr($command,1)];
$replyMsg=$this->$func($params,$connection);
}
else{
$replyMsg=$this->getPromptMessage("cmer1");
}
}
else{
//All other messages - possibly processed with NPL
}
$this->performSenderAction(2);
$replyMsg = json_encode($replyMsg);
$actionCompletedOrh->sendJustTextMessage($replyMsg,$access_token);
}
Вы видите что-нибудь, что я мог бы улучшить? Пожалуйста, дайте мне знать, что и почему в комментариях!