botman/studio
BotMan Studio is a Laravel-based starter kit for building chatbots with the BotMan PHP framework. It provides a ready-to-use application, integrated chat testing interface, and common messaging drivers so you can scaffold, run, and debug bots quickly.
BotMan Studio is not a package you composer require into an existing app — it's a full Laravel skeleton (Laravel 5.7-era) with botman/botman, botman/laravel, and the web widget pre-wired. Treat it as a starting project or as a reference implementation.
composer create-project --prefer-dist botman/studio botman-app
cd botman-app
cp .env.example .env && php artisan key:generate
php artisan serve
Set APP_URL correctly in .env (the widget and every webhook URL depend on it), then drop driver credentials into config/botman/<driver>.php (Slack, Telegram, Facebook, Twilio, Nexmo) — Studio keeps them in a config/botman/ folder rather than a single file.
Where to look first, in order:
routes/botman.php — the bot's brain. All hears / receives / on handlers live here by default.app/Http/Controllers/BotManController.php — handle() calls app('botman')->listen(); startConversation() renders the chat widget.routes/web.php — the botman and botman/chat routes, plus the CSRF exception in app/Http/Middleware/VerifyCsrfToken.php.config/botman/config.php — conversation_cache_time and user_cache_time (both in minutes).First use case — prove the loop in 60 seconds, no channel required:
php artisan botman:tinker
BotMan > hi
Bot > Hey there!
Add a handler and retry:
// routes/botman.php
$botman->hears('hello', function ($bot) {
$bot->reply('Hey there!');
});
$botman->hears('order {id}', function ($bot, $id) {
$bot->reply("Looking up order #{$id}...");
});
Then open https://your-app.test/botman for the built-in web widget, and finally point a real channel at https://your-app.test/botman (Telegram setWebhook, Slack Event Subscriptions, Facebook callback URL + verify token). php artisan botman:list shows what's registered.
Three handler types, and they are not interchangeable:
$botman->hears('status', StatusHandler::class); // pattern match, first-match-wins
$botman->receives(['text','image'], function ($bot) {}); // catch-all by message type
$botman->on('conversation.started', function ($bot, $payload) {}); // driver/platform events
hears supports {placeholders} and regex. Registration order matters — put specific patterns above broad ones, and keep a single receives fallback at the bottom for the "I didn't understand" path.
Conversations for anything multi-step. Don't build state machines out of hears handlers; use a Conversation class. This is the single biggest maintainability win:
class OnboardConversation extends Conversation
{
public function run()
{
$this->ask('What is your email?', function (Answer $answer) {
$this->email = $answer->getText();
$this->askForLocation('Where should we ship?', function (Answer $loc) {
// $loc->getLatitude(), getLongitude()
$this->say('Saved.');
$this->stop();
});
});
}
}
$botman->hears('onboard', function ($bot) {
$bot->startConversation(new OnboardConversation());
});
The Answer object gives you getText(), getValue(), getImages(), getLocation(), plus the raw payload via getMessage()->getPayload(). Use $this->repeat(), $this->stop(), and $this->bot inside conversations.
Middleware is where you put cross-cutting concerns — logging every inbound message, resolving the app user from $bot->getMessage()->getSender(), rate limiting, analytics. Register globally in the service provider or per-handler:
$botman->middleware->heard(new LogIncomingMessage());
$botman->middleware->sending(new AppendSignature());
Keep handlers thin. The pattern that scales is handler → service/action → reply:
$botman->hears('invoice {id}', function ($bot, $id) {
$invoice = app(InvoiceLookup::class)->find($id, $bot->getMessage()->getSender()['id']);
$bot->reply($invoice ? "Total: {$invoice->total}" : "Not found.");
});
Rich replies — use the template classes rather than hand-rolling payloads: Button, ButtonTemplate, ListTemplate, Attachment, and $bot->replyWithImage(). $bot->typesAndWaits(1) adds a human-feeling delay.
Testing strategy: botman:tinker for the conversational logic, then each channel's own sandbox (Telegram test bot, Slack dev workspace, Facebook test page) for anything involving buttons, attachments, or webhook verification. For CI, POST a recorded driver payload to the /botman route and assert on the outgoing response — that's the only thing that exercises the real HTTP path.
botman/botman + botman/laravel into your real app and copy the controller/routes/config structure. That's the realistic "day-to-day leverage" answer.'botman' must be in VerifyCsrfToken::$except. If the widget posts fine but the bot never answers, check this first.conversation_cache_time (minutes). With the file driver and more than one app server, users randomly fall out of conversations mid-flow. Use Redis/DB cache in production.config/botman/*.php into the config namespace, but once you run php artisan config:cache, editing those files does nothing until you re-cache. In local dev, use config:clear./botman URL with no verification is an open relay into your bot logic.$bot->getMessage()->getPayload() and $bot->getDriver()->getName() dumped to storage/logs/laravel.log answer 90% of "why did it not match" questions. Note that botman:tinker does not simulate real driver payloads — attachments, buttons, and postbacks all look different in tinker than they do live, so never conclude a channel bug is fixed from tinker alone.Driver classes, custom Middleware, conversation classes, and overriding the BotManWidget view for branding. The service provider (BotManServiceProvider) is the seam where you'd swap the singleton if you outgrow the defaults.How can I help you explore Laravel packages today?