快速入门
Botman 是什么
开始之前,我们需要花一点篇幅先了解下 Botman 是什么。官方介绍如下:
Botman 是一个与框架无关的、可以在不同消息平台轻松实现聊天机器人的 PHP 库,这些消息平台包括但不限于 Slack、Telegram、Microsoft Bot Framework、Nexmo、HipChat、Facebook Messenger 以及微信等。
Botman Studio
关于 Botman 的详细使用可以参考官方文档,我们可以在已有项目中单独安装 Botman 库来使用,当然,如果你对 Laravel 熟悉的话,也可以通过官方提供的工作室项目 Botman Studio 来快速上手。本教程就是基于 Botman Studio 的,通过 Composer 来安装:
composer create-project --prefer-dist botman/studio dogchat
dogchat
http://dogchat.test
Tinkerhttp://dogchat.test/botman/tinkerHiHello!
这足以说明我们的系统已经可以正常运行了,下面我们就来定义更多的指令以完成更复杂的功能。
创建指令
从所有品种中返回随机图片
app/Services/DogService
<?php
namespace App\Services;
use Exception;
use GuzzleHttp\Client;
class DogService
{
// 获取随机狗狗图片的接口
const RANDOM_ENDPOINT = 'https://dog.ceo/api/breeds/image/random';
/**
* Guzzle client.
*
* @var Client
*/
protected $client;
/**
* DogService constructor
*/
public function __construct()
{
$this->client = new Client();
}
/**
* 获取并返回随机图片
*
* @return string
*/
public function random()
{
try {
// Decode the json response.
$response = json_decode(
// Make an API call an return the response body.
$this->client->get(self::RANDOM_ENDPOINT)->getBody()
);
// Return the image URL.
return $response->message;
} catch (Exception $e) {
// 如果出错,返回以下错误信息给用户
return 'An unexpected error occurred. Please try again later.';
}
}
}
AllBreedsController
php artisan make:controller AllBreedsController
AllBreedsController
<?php
namespace App\Http\Controllers;
use App\Services\DogService;
use Illuminate\Http\Request;
class AllBreedsController extends Controller
{
/**
* Controller constructor
*
* @return void
*/
public function __construct()
{
$this->photos = new DogService();
}
/**
* Return a random dog image from all breeds.
*
* @return void
*/
public function random($bot)
{
// $this->photos->random() is basically the photo URL returned from the service.
// $bot->reply is what we will use to send a message back to the user.
$bot->reply($this->photos->random());
}
}
routes/botman.php
$botman->hears('random', AllBreedsController::class . '@random');
http://dogchat.test/botman/tinkerrandom
从特定品种中返回随机图片
AllBreedsController
/**
* Return a random dog image from a given breed.
*
* @return void
*/
public function byBreed($bot, $name)
{
// Because we used a wildcard in the command definition, Botman will pass it to our method.
// Again, we let the service class handle the API call and we reply with the result we get back.
$bot->reply($this->photos->byBreed($name));
}
DogServicebyBread
/**
* Fetch and return a random image from a given breed.
*
* @param string $breed
* @return string
*/
public function byBreed($breed)
{
try {
// We replace %s in our endpoint with the given breed name.
$endpoint = sprintf(self::BREED_ENDPOINT, $breed);
$response = json_decode(
$this->client->get($endpoint)->getBody()
);
return $response->message;
} catch (Exception $e) {
return "Sorry I couldn\"t get you any photos from $breed. Please try with a different breed.";
}
}
BREED_ENDPOINT
// The endpoint we will hit to get a random image by a given breed name.
const BREED_ENDPOINT = 'https://dog.ceo/api/breed/%s/images/random';
routes/botman.php
$botman->hears('b {breed}', AllBreedsController::class . '@byBreed');
http://dogchat.test/botman/tinker
通过给定品种+子品种返回随机图片
SubBreedController
php artisan make:controller SubBreedController
SubBreedController
<?php
namespace App\Http\Controllers;
use App\Services\DogService;
class SubBreedController extends Controller
{
/**
* Controller constructor
*
* @return void
*/
public function __construct()
{
$this->photos = new DogService();
}
/**
* Return a random dog image from all breeds.
*
* @return void
*/
public function random($bot, $breed, $subBreed)
{
$bot->reply($this->photos->bySubBreed($breed, $subBreed));
}
}
DogService
// The endpoint we will hit to get a random image by a given breed name and its sub-breed.
const SUB_BREED_ENDPOINT = 'https://dog.ceo/api/breed/%s/%s/images/random';
bySubBreed
/**
* Fetch and return a random image from a given breed and its sub-breed.
*
* @param string $breed
* @param string $subBreed
* @return string
*/
public function bySubBreed($breed, $subBreed)
{
try {
$endpoint = sprintf(self::SUB_BREED_ENDPOINT, $breed, $subBreed);
$response = json_decode(
$this->client->get($endpoint)->getBody()
);
return $response->message;
} catch (Exception $e) {
return "Sorry I couldn\"t get you any photos from $breed. Please try with a different breed.";
}
}
routes/botman.php
$botman->hears('s {breed}:{subBreed}', SubBreedController::class . '@random');
http://dogchat.test/botman/tinker
返回提供操作选项的会话
DefaultConversation
php artisan botman:make:conversation DefaultConversation
DefaultConversation
<?php
namespace App\Http\Conversations;
use App\Services\DogService;
use BotMan\BotMan\Messages\Conversations\Conversation;
use BotMan\BotMan\Messages\Incoming\Answer;
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
use BotMan\BotMan\Messages\Outgoing\Question;
class DefaultConversation extends Conversation
{
/**
* 启动带操作选项会话的问题
*/
public function defaultQuestion()
{
// We first create our question and set the options and their values.
$question = Question::create('Huh - you woke me up. What do you need?')
->addButtons([
Button::create('Random dog photo')->value('random'),
Button::create('A photo by breed')->value('breed'),
Button::create('A photo by sub-breed')->value('sub-breed'),
]);
// We ask our user the question.
return $this->ask($question, function (Answer $answer) {
// Did the user click on an option or entered a text?
if ($answer->isInteractiveMessageReply()) {
// We compare the answer to our pre-defined ones and respond accordingly.
switch ($answer->getValue()) {
case 'random':
$this->say((new DogService())->random());
break;
case 'breed':
$this->askForBreedName();
break;
case 'sub-breed':
$this->askForSubBreed();
break;
}
}
});
}
/**
* Ask for the breed name and send the image.
*
* @return void
*/
public function askForBreedName()
{
$this->ask('What\'s the breed name?', function (Answer $answer) {
$name = $answer->getText();
$this->say((new DogService())->byBreed($name));
});
}
/**
* Ask for the breed name and send the image.
*
* @return void
*/
public function askForSubBreed()
{
$this->ask('What\'s the breed and sub-breed names? ex:hound:afghan', function (Answer $answer) {
$answer = explode(':', $answer->getText());
$this->say((new DogService())->bySubBreed($answer[0], $answer[1]));
});
}
/**
* Start the conversation
*
* @return void
*/
public function run()
{
// This is the boot method, it's what will be excuted first.
$this->defaultQuestion();
}
}
BotmanControllerstartConversation
/**
* Loaded through routes/botman.php
* @param BotMan $bot
*/
public function startConversation(BotMan $bot)
{
$bot->startConversation(new DefaultConversation());
}
这样我们就可以在测试页面中测试了:
点击右侧会话选项就会返回相应的回复。
响应未注册指令
很多时候用户输入指令可能后端并未实现,所以我们需要创建一个指令对该类消息进行兜底处理。先创建一个用于处理未注册指令的控制器:
php artisan make:controller FallbackController
然后编写该控制器代码如下:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
class FallbackController extends Controller
{
/**
* Respond with a generic message.
*
* @param Botman $bot
* @return void
*/
public function index($bot)
{
$bot->reply('Sorry, I did not understand these commands. Try: \'Start Conversation\'');
}
}
routes/botman.php
$botman->fallback(FallbackController::class . '@index');
在测试页面中测试任意未注册指令,返回如下:
在微信公众号中集成 Botman
web
php artisan botman:list-drivers
返回结果如下:
wechat
php artisan botman:install-driver wechat
config/botmanwechatapp_idapp_keyverification
下面我们通过微信公众平台测试帐号,如果没有注册的话按照系统提示完成注册流程,扫描登录成功后进入管理页面可以看到如下信息:
appIDapp_idappsecretapp_keyTokenverificationToken
ngrokvalet share
cd dogchat
php artisan serve
ngrok http 8000
URL
以下是我的测试结果:

wechat