Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Driver Web Laravel Package

botman/driver-web

Web driver for BotMan that connects PHP chatbots to websites, handling incoming browser messages and sending replies. Add interactive chatbot conversations to Laravel or PHP apps with a simple, framework-friendly integration.

Deep Wiki
Context7

Getting Started

Minimal install

composer require botman/botman botman/driver-web
php artisan vendor:publish --provider="BotMan\BotMan\BotManServiceProvider"
php artisan vendor:publish --provider="BotMan\Drivers\Web\WebServiceProvider"

The second publish gives you config/botman/web.php (at minimum: chat_view, chat_api_url, user_cache_time) and a copy of the widget Blade view under resources/views/vendor/botman/. botman/botman brings its own service provider (auto-discovered on Laravel 5.5+); WebServiceProvider registers the driver with DriverManager and merges the web config.

Wire the endpoint

// routes/web.php
Route::match(['get', 'post'], 'botman', [BotManController::class, 'handle']);
Route::get('botman/tinker', [BotManController::class, 'tinker']); // dev only
// app/Http/Controllers/BotManController.php
use BotMan\BotMan\BotMan;
use BotMan\BotMan\Drivers\DriverManager;
use BotMan\Drivers\Web\WebDriver;

public function handle()
{
    DriverManager::loadDriver(WebDriver::class); // harmless idempotent safety net
    app('botman')->listen();                     // driver emits the JSON response itself
}

public function tinker()
{
    abort_unless(app()->environment('local'), 404); // never ship this open
    return view('botman-tinker');
}

Drop the widget into your layout

{{-- resources/views/layouts/app.blade.php --}}
<meta name="csrf-token" content="{{ csrf_token() }}">
...
@include('botman::chat')   {{-- or 'botman.chat' if you published and edited it --}}

Where to look first, in order

  1. config/botman/web.php — endpoint path, view name, cache timing.
  2. resources/views/vendor/botman/chat.blade.php — read it top-to-bottom; it's the whole frontend contract (endpoint URL, widget options, markup hooks). This is what you'll fork for branding.
  3. app/Http/Controllers/BotManController.php — the only server entry point.
  4. vendor/botman/driver-web/src/WebDriver.php and WebUser.php — how the request is matched, how the user is identified (session-derived), how payloads are serialized.
  5. /botman/tinker — a standalone chat page hitting the same endpoint. Fastest smoke test before you touch your layout.

First use case

An FAQ/support widget on a marketing or SaaS layout. Register intents in a routes/botman.php file (or a service provider's boot()) that the controller requires:

$bot = app('botman');

$bot->hears('pricing', fn (BotMan $bot) => $bot->reply('Plans start at $19/mo. Want the comparison table?'));
$bot->hears('(demo|trial)', fn (BotMan $bot) => $bot->startConversation(new DemoTrialConversation()));
$bot->hears('{anything}', fn (BotMan $bot) => $bot->reply("I'll pass that to a human."));
$bot->fallback(fn (BotMan $bot) => $bot->reply("I didn't catch that — try 'pricing' or 'demo'."));

Implementation Patterns

Controller stays thin; intents live in their own file. Put require base_path('routes/botman.php'); inside BotManController@handle (before listen()), so intents are versioned and reloadable without touching the controller. listen() reads the HTTP request, dispatches to matching hears/conversation, and returns the driver's JSON response — don't wrap it in a transformer or add return response()->json(...).

Conversation classes are the workhorse. Multi-step flows (BotMan\BotMan\Conversations\Conversation with run(), ask(), $this->say()) are the right tool for onboarding, lead capture, and anything with slots. Start with $bot->startConversation(new LeadCaptureConversation()); the web driver stores conversation state in the Laravel cache, so the next message from the same session resumes the thread. Keep flow state in conversation properties or your own cache keys, not in the chat payload.

Identify the human behind the widget. The web driver keys users off the Laravel session (WebUser derives its id from the session), so if you also need the authenticated User, do it in middleware:

$bot->middleware->heard(function ($message, $next) {
    if (auth()->check()) {
        $message->addExtras(['app_user_id' => auth()->id()]);
        // or: Cache::put("botman_user_".session()->getId(), auth()->id(), 3600);
    }
    return $next($message);
});

Same hook is where you log transcripts, tag conversations for analytics, or gate the widget to paying customers only.

Serve a custom frontend against the same endpoint. The wire protocol is plain JSON: POST { driver: "web", message: "...", ... } to /botman, receive { messages: [ { type: "text", text: "..." }, { type: "text", attachment: {...} }, ... ], status: ... }. That means you can throw away the bundled Vue 2 widget and ship a React/Livewire/Alpine chat panel that posts to the identical route — often the best move when the default skin doesn't match your design system. Keep the driver: "web" field and the session cookie; everything else is yours.

Rich replies. Buttons and attachments (Button::create(), Image::url(), Video, Audio) are serialized into the messages array and rendered by the bundled widget. Verify each element type against the widget version you have before designing a flow around lists or carousels — the 2018-era bundle is conservative, and custom frontends need per-type rendering code anyway.

State store must be shared. Conversation cache (conversation_cache_time) and user cache (user_cache_time) live in Laravel's cache. On multi-server infra, CACHE_DRIVER=file means a user's flow "forgets" whenever they land on another node. Use Redis/memcached and keep the TTLs generous for long flows.

Feature tests hit the endpoint directly.

$this->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class);

$this->postJson('/botman', ['driver' => 'web', 'message' => 'pricing'])
     ->assertOk()
     ->assertJsonFragment(['text' => 'Plans start at $19/mo.']);

Seed the response by pointing the test at a dedicated routes/botman.php if needed, and clear conversation cache between assertions (Cache::flush() in setUp).

Throttle and observe. Add throttle:30,1 to the route, log every inbound payload via $bot->middleware->received(...), and emit a metric on unmatched fallbacks — fallback volume is your best signal that the intent set is wrong.

Gotchas and Tips

  • 419s are the #1 support ticket. The widget posts via AJAX; either ship <meta name="csrf-token"> in the layout or add /botman to VerifyCsrfToken::$except. Do not put the route behind the api middleware group — it has no session, and the web driver has no user identity without one.
  • Stateless API routes = cross-user bleed. If session is missing (SESSION_DRIVER=array, no cookies), user and conversation caches can't be keyed correctly. /botman belongs in web middleware, always.
  • chat_api_url is relative. Under a subdirectory deploy or a non-root APP_URL, verify the widget actually hits /subdir/botman. Inspect the rendered HTML — the endpoint is baked into the Blade view, not resolved at runtime per request.
  • The published view is a frozen Vue 2 bundle, not an npm package. Roughly 200 KB of pre-built JS with no build pipeline. Branding means editing the Blade view and overriding CSS classes; anything deeper means replacing the widget (see the custom-frontend pattern above). Don't try to npm install your way into it.
  • No real server push. The driver is request/response; $bot->say('…', $userId) to a web user only surfaces on the next inbound request. Proactive notifications (order shipped, agent replied) belong on a channel that actually pushes, or on a custom polling loop you own.
  • Tinker route is an unauthenticated chat endpoint. abort_unless(app()->environment('local'), 404) and never let it ship. Same for the default widget view if you keep it on an internal-only page.
  • Conversations expire silently. Default cache TTLs (30–40 min) mean a flow restarts mid-way if the user wanders off. Set conversation_cache_time deliberately and always give run() a resumable entry point plus a "start over" intent.
  • Intent order matters. hears() is first-match-wins in registration order; a greedy {anything} catch-all registered early swallows everything. Register the fallback last, and prefer anchored regexes ('^pricing$') over bare keywords for short words.
  • Debugging checklist, in order: (1) does /botman/tinker work? → frontend problem. (2) does POST /botman with driver=web and a session cookie return messages? → middleware/session problem. (3) is the cache key present after the request? → driver registration problem (DriverManager::loadDriver not called, or WebServiceProvider missing from config/app.php on pre-5.5 apps). (4) does the hears fire on received but not reply? → intent matching/order.
  • Maintenance risk is real. Last release March 2018. Check composer why-not against your Laravel version before committing — on Laravel 10/11 you may need a fork, a replace, or --ignore-platform-reqs. Pin the BotMan pair (botman/botman + botman/driver-web) together and budget a spike to validate on a branch rather than discovering it at deploy time.
  • Config caching is safe; route caching with closure intents is not. If you register hears() closures in routes/web.php, route:cache will fail. That's another argument for keeping intents in routes/botman.php required from the controller.
  • Sanitize outbound content. The bundled widget's text interpolation escapes HTML, but you are injecting user-supplied strings (names, ticket text) into replies — treat every $bot->reply() argument as user-facing output and strip/escape anything that didn't originate in your code.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core