PHP code example of bootdesk / chat-sdk-laravel

1. Go to this page and download the library: Download bootdesk/chat-sdk-laravel library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

bootdesk / chat-sdk-laravel example snippets


return [

    // The display name your bot uses when posting messages.
    'user_name' => env('BOT_USERNAME', 'Bot'),

    // Platform adapters to enable. Only adapters whose Composer package
    // is installed (class_exists) will be loaded. For multi-tenant
    // setups, omit the platform here and use an AdapterResolver instead.
    'adapters' => [
        // 'slack' => [
        //     'bot_token' => env('SLACK_BOT_TOKEN'),
        //     'signing_secret' => env('SLACK_SIGNING_SECRET'),
        // ],
        // 'telegram' => [
        //     'bot_token' => env('TELEGRAM_BOT_TOKEN'),
        // ],
        // 'whatsapp' => [
        //     'access_token' => env('WHATSAPP_ACCESS_TOKEN'),
        //     'app_secret' => env('WHATSAPP_APP_SECRET'),
        //     'phone_number_id' => env('WHATSAPP_PHONE_NUMBER_ID'),
        //     'verify_token' => env('WHATSAPP_VERIFY_TOKEN'),
        // ],
        // 'discord' => [
        //     'bot_token' => env('DISCORD_BOT_TOKEN'),
        //     'application_id' => env('DISCORD_APPLICATION_ID'),
        //     'public_key' => env('DISCORD_PUBLIC_KEY'),
        // ],
        // 'messenger' => [
        //     'page_access_token' => env('MESSENGER_PAGE_ACCESS_TOKEN'),
        //     'app_secret' => env('MESSENGER_APP_SECRET'),
        //     'verify_token' => env('MESSENGER_VERIFY_TOKEN'),
        // ],
        // 'web' => [
        //     'user_name' => env('BOT_USERNAME', 'Bot'),
        // ],
        // 'github' => [
        //     'auth_token' => env('GITHUB_TOKEN'),
        //     'webhook_secret' => env('GITHUB_WEBHOOK_SECRET'),
        // ],
        // 'linear' => [
        //     'api_key' => env('LINEAR_API_KEY'),
        //     'webhook_secret' => env('LINEAR_WEBHOOK_SECRET'),
        // ],
    ],

    // State persistence backed by the Laravel Cache facade. The cache
    // store is resolved from the facade at runtime — configure it in
    // config/cache.php as usual.
    'state' => [
        'prefix' => env('CHAT_STATE_PREFIX', 'chat:'),
    ],

    // Global handler classes registered on every Chat instance regardless
    // of adapter. Each class must implement a register($chat) method.
    'handlers' => [
        // \App\Chat\GlobalHandlers::class,
    ],

    // Adapter-specific handler groups. Only the matching group is
    // registered per webhook request, alongside global handlers above.
    'handler_groups' => [
        // 'slack' => [
        //     \App\Chat\SlackHandler::class,
        // ],
        // 'telegram' => [
        //     \App\Chat\TelegramHandler::class,
        // ],
    ],

    // How to handle concurrent messages for the same thread.
    // Core strategies: drop (default), queue, debounce, concurrent.
    // Laravel uses QueueConcurrencyHandler to dispatch jobs for async processing.
    'concurrency' => env('CHAT_CONCURRENCY', 'drop'),

    // Scope for distributed locks: 'thread' (default) or 'channel'.
    // Use 'channel' for platforms like WhatsApp/Telegram where
    // conversations are per-channel (one conversation per phone number).
    'lock_scope' => env('CHAT_LOCK_SCOPE', 'thread'),

    // Cross-platform per-user message persistence. Requires an
    // IdentityResolver bound to the container (IdentityResolver::class).
    'transcripts' => null,

];

// routes/web.php or routes/api.php
use BootDesk\ChatSDK\Laravel\Http\Controllers\WebhookController;

Route::match(['get', 'post'], '/api/webhooks/{adapter}', WebhookController::class);

// app/Chat/ChatHandlers.php
namespace App\Chat;

use BootDesk\ChatSDK\Core\Chat;
use BootDesk\ChatSDK\Core\MessageContext;
use BootDesk\ChatSDK\Laravel\Contracts\ChatHandler as ChatHandlerContract;

class ChatHandlers implements ChatHandlerContract
{
    public function register(Chat $chat): void
    {
        $chat->onNewMessage('/^hello$/i', function (MessageContext $ctx) {
            $ctx->thread->post('Hey!');
        });

        $chat->fallback(function (MessageContext $ctx) {
            $ctx->thread->post("I don't understand that.");
        });
    }
}

// Global — fires for every adapter
'handlers' => [\App\Chat\ChatHandlers::class],

'handler_groups' => [
    'slack' => [\App\Chat\SlackHandlers::class],
    'telegram' => [\App\Chat\TelegramHandlers::class],
],

use Illuminate\Http\Request;
use Psr\Http\Message\ServerRequestInterface;

class ChannelAwareController extends WebhookController
{
    protected function resolveGroups(string $adapter, Request $request, ServerRequestInterface $psrRequest): array
    {
        $channel = $request->input('channel_id');

        return match ($channel) {
            'C001' => ['slack', 'internal-support'],
            'C002' => ['slack', 'customer-support'],
            default => [$adapter],
        };
    }
}

use BootDesk\ChatSDK\Laravel\Contracts\ChatHandlerWithRequest;

class TenantAwareHandler implements ChatHandlerWithRequest
{
    public function register(Chat $chat, ?ServerRequestInterface $request = null): void
    {
        $tenant = $request?->getHeaderLine('X-Tenant') ?? 'default';

        $chat->onNewMessage('/bill/', function (MessageContext $ctx) use ($tenant) {
            // tenant-specific billing flow
        });
    }
}

use BootDesk\ChatSDK\Core\Contracts\WebhookMiddleware;
use BootDesk\ChatSDK\Core\Contracts\ReceivingMiddleware;
use BootDesk\ChatSDK\Core\Contracts\SendingMiddleware;

class ChatHandlers
{
    public function register(Chat $chat): void
    {
        // Intercept raw webhook before parsing
        $chat->addWebhookMiddleware(new class implements WebhookMiddleware {
            public function handle(ServerRequestInterface $request, callable $next): ResponseInterface {
                logger()->info('Webhook received', ['path' => $request->getUri()->getPath()]);
                return $next($request);
            }
        });

        // Transform inbound messages before handlers
        $chat->addReceivingMiddleware(new class implements ReceivingMiddleware {
            public function handle(Message $message, Adapter $adapter, callable $next): ?Message {
                // Return null to drop the message
                if (str_contains($message->text, 'blocked')) {
                    return null;
                }
                return $next($message);
            }
        });

        // Transform outbound messages before delivery
        $chat->addSendingMiddleware(new class implements SendingMiddleware {
            public function handle(string $threadId, PostableMessage $message, Adapter $adapter, string $operation, callable $next): ?SentMessage {
                logger()->info('Sending message', ['thread' => $threadId, 'operation' => $operation]);
                return $next($message);
            }
        });
    }
}

// AppServiceProvider::register()
use BootDesk\ChatSDK\Core\Contracts\IdentityResolver;
use BootDesk\ChatSDK\Core\Author;

$this->app->bind(IdentityResolver::class, fn () => new class implements IdentityResolver {
    public function resolve(Author $author): ?string {
        return $author->id;
    }
});

// config/chat.php
'transcripts' => ['max_messages' => 100, 'ttl_ms' => 2592000000],

$this->app->bind(TranscriptsApi::class, function ($app) {
    return new MyRedisTranscriptsApi(
        state: $app->make(StateAdapter::class),
        config: ['max_messages' => 200],
    );
});

$transcripts = $chat->getTranscripts();

// List history for a user
$entries = $transcripts->list('user:U123');

// Each entry has: id, text, authorId, threadId, timestamp, direction

// Count
$count = $transcripts->count('user:U123');

// Delete
$transcripts->delete('user:U123');

// app/Chat/MultiTenantAdapterResolver.php
namespace App\Chat;

use BootDesk\ChatSDK\Core\Contracts\Adapter;
use BootDesk\ChatSDK\Core\Contracts\AdapterResolver;
use BootDesk\ChatSDK\Slack\SlackAdapter;
use BootDesk\ChatSDK\Telegram\TelegramAdapter;
use Illuminate\Support\Facades\DB;
use Psr\Http\Message\ServerRequestInterface;

class MultiTenantAdapterResolver implements AdapterResolver
{
    public function resolve(string $name, ?ServerRequestInterface $request): ?Adapter
    {
        // Extract tenant from request (header, subdomain, route param, etc.)
        // When called from a job, $request is null - use other context (job payload, auth, etc.)
        $tenantId = $request?->getHeaderLine('X-Tenant-ID')
            ?? $this->getTenantFromContext();

        if ($tenantId === null || $tenantId === '') {
            return null;
        }

        // Load tenant-specific credentials from database
        $config = DB::table('tenant_chat_configs')
            ->where('tenant_id', $tenantId)
            ->where('adapter', $name)
            ->first();

        if (! $config) {
            return null;
        }

        // Instantiate adapter with tenant credentials
        return match ($name) {
            'slack' => new SlackAdapter(
                botToken: $config->credentials['bot_token'],
                httpClient: app(\Psr\Http\Client\ClientInterface::class),
                signingSecret: $config->credentials['signing_secret'] ?? null,
            ),
            'telegram' => new TelegramAdapter(
                botToken: $config->credentials['bot_token'],
                httpClient: app(\Psr\Http\Client\ClientInterface::class),
                secretToken: $config->credentials['secret_token'] ?? null,
            ),
            default => null,
        };
    }
}

// app/Providers/AppServiceProvider.php
use BootDesk\ChatSDK\Core\Contracts\AdapterResolver;

public function register(): void
{
    $this->app->bind(
        AdapterResolver::class,
        \App\Chat\MultiTenantAdapterResolver::class
    );
}

use BootDesk\ChatSDK\Laravel\ChatFactory;

class MessageController
{
    public function __construct(
        private ChatFactory $chatFactory,
    ) {}

    public function send()
    {
        $chat = $this->chatFactory->default(); // global handlers only
        $chat->thread('slack:C123')->post('Hello!');
    }
}

$chat = $this->chatFactory->forGroup('slack'); // global + slack handlers
$chat->handleWebhook('slack', $psrRequest);

$chat = $this->chatFactory->forGroups(['slack', 'internal-support']); // global + both groups

use BootDesk\ChatSDK\Core\Exceptions\AdapterException;
use BootDesk\ChatSDK\Core\Exceptions\AuthenticationException;
use BootDesk\ChatSDK\Core\Exceptions\RateLimitException;
use Illuminate\Http\Request;

public function register(): void
{
    $this->renderable(function (AuthenticationException $e, Request $request) {
        return response()->json(['error' => 'Unauthorized'], 401);
    });

    $this->renderable(function (RateLimitException $e, Request $request) {
        return response()->json(['error' => 'Rate limited'], 429);
    });

    $this->renderable(function (AdapterException $e, Request $request) {
        Log::error('Chat adapter error', [
            'message' => $e->getMessage(),
            'adapter' => $request->route('adapter'),
        ]);

        return response()->json(['error' => 'Adapter failed'], 500);
    });
}
bash
php artisan chat:install
bash
php artisan queue:work