1. Go to this page and download the library: Download wekser/laragram 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/ */
wekser / laragram example snippets
Schema::table('laragram_users', function (Blueprint $table) {
$table->string('role')->default('user')->after('settings')->index();
$table->boolean('is_active')->default(true)->after('role')->index();
$table->timestamp('deactivated_at')->nullable()->after('is_active');
});
Schema::table('laragram_sessions', function (Blueprint $table) {
$table->bigInteger('chat_id')->nullable()->after('user_id');
$table->unsignedBigInteger('thread_id')->default(0)->after('chat_id');
});
// Existing rows are private chats: the chat is the user.
DB::table('laragram_sessions')
->join('laragram_users', 'laragram_users.id', '=', 'laragram_sessions.user_id')
->update(['laragram_sessions.chat_id' => DB::raw('laragram_users.uid')]);
Schema::table('laragram_sessions', function (Blueprint $table) {
$table->unique(['user_id', 'chat_id', 'thread_id']);
$table->index(['user_id', 'chat_id', 'thread_id', 'last_activity']);
});
use Wekser\Laragram\Facades\BotRoute;
// Match /start from any station
BotRoute::get('message')
->contains('/start')
->call([StartController::class, 'index']);
// Match any text when user is at 'ask_name' station
BotRoute::get('message')
->from('ask_name')
->call([OnboardingController::class, 'saveName']);
// Match callback with a named param, admin only
BotRoute::get('callback_query')
->from('home')
->contains('/action {id}')
->role('admin')
->call([AdminController::class, 'action']);
// Catch-all fallback
BotRoute::fallback()->call([StartController::class, 'fallback']);
use Wekser\Laragram\BotRequest;
use Wekser\Laragram\BotResponse;
use Wekser\Laragram\Models\User;
class StartController extends Controller
{
public function __construct(protected BotResponse $response) {}
public function index(BotRequest $request, User $user): BotResponse
{
return $this->response
->text("Hello, {$user->first_name}!")
->redirect('home');
}
}
$request->get('text'); // dot-notation access to any update field
$request->input('id'); // named {param} from the matched route pattern
$request->message(); // the message sub-object
$request->callbackQuery(); // the callback_query sub-object
$request->validate([...]); // Laravel validation on the update payload
$response->text('Hello!') // sendMessage (HTML by default)
$response->text('Hello!', 'MarkdownV2') // MarkdownV2 parse mode
$response->text('Hello!', null) // no escaping
$response->view('welcome', ['name' => 'Alice']) // render a view directory
$response->photo($fileId, caption: 'A photo') // sendPhoto
$response->document($fileId) // sendDocument
$response->edit('Updated text') // editMessageText
$response->answer('Done!', showAlert: true) // answerCallbackQuery
$response->delete() // deleteMessage
$response->react('👍') // setMessageReaction
$response->action('typing') // sendChatAction
$response->invoice(Invoice::make()...) // sendInvoice (see Payments)
$response->inlineResults(InlineResults::make()...) // answerInlineQuery (see Inline Mode)
$response->keyboard([...]) // attach reply_markup (call after content)
$response->thread(42) // send into a forum topic (see Group Chats)
$response->redirect('next_station') // move user to a new station
public function welcome(): array
{
return [
$this->response->view('greeting'),
$this->response->text('Here is a quick tip 👇'),
$this->response->photo($fileId, caption: 'And a picture')->redirect('home'),
];
}
use Wekser\Laragram\Telegram\Keyboards\InlineKeyboard;
use Wekser\Laragram\Telegram\Keyboards\ReplyKeyboard;
use Wekser\Laragram\Telegram\Keyboards\ForceReply;
$response->text('Choose:')->keyboard(
InlineKeyboard::make()
->button('Yes', 'confirm')
->button('No', 'cancel')
->row()
->href('Open site', 'https://example.com')
->webApp('Open Mini App', 'https://example.com/app')
->toArray()
);
$response->text('Choose:')->keyboard(
ReplyKeyboard::make()
->button('Option A')->button('Option B')
->row()
->requestLocation('📍 Send my location') // also requestContact / requestPoll / requestUser / requestChat
->resize()->oneTime()
->toArray()
);
ReplyKeyboard::remove(); // ['remove_keyboard' => true]
ForceReply::make()->placeholder('Type here…')->toArray();
// routes/laragram/routes.php
BotRoute::get('message')->contains('/start')->call([Ctrl::class, 'start']);
BotRoute::get('message')->from('ask_name')->call([Ctrl::class, 'saveName']);
BotRoute::get('message')->from('ask_email')->call([Ctrl::class, 'saveEmail']);
// controller
public function start(): array
{
// Send several messages at once; the next station comes from the last
// response that calls redirect() (here, the question).
return [
$this->response->text('Welcome! 👋'),
$this->response->text("What's your name?")->redirect('ask_name'),
];
}
public function saveName(BotRequest $request): BotResponse
{
// store name ...
return $this->response->text('Now your email:')->redirect('ask_email');
}
// Restrict routes to a chat type (default: any)
BotRoute::get('message')->contains('/rules')->inGroups()->call([Ctrl::class, 'rules']);
BotRoute::get('message')->contains('/settings')->inPrivate()->call([Ctrl::class, 'settings']);
BotRoute::get('message')->contains('/kick')->chat('supergroup')->call([Ctrl::class, 'kick']);
// Inspect the chat in a handler
public function rules(BotRequest $request): BotResponse
{
$request->isGroup(); // group || supergroup
$request->isPrivate();
$request->chatType(); // 'private' | 'group' | 'supergroup' | 'channel'
return $this->response->view('rules');
}
// Restrict a route to one or more topics
BotRoute::get('message')->contains('/deploy')->thread(42)->call([OpsController::class, 'deploy']);
public function deploy(BotRequest $request): array
{
$request->threadId(); // 42 — the topic, or null outside one
$request->isTopicMessage();
return [
$this->response->text('Deploying…'), // → back into topic 42
$this->response->text('Logged')->thread(7), // → into topic 7 instead
$this->response->text('Announcement')->thread(null), // → the General topic
];
}
use Wekser\Laragram\Facades\BotResponse;
use Wekser\Laragram\Facades\BotScene;
use Wekser\Laragram\Telegram\Keyboards\InlineKeyboard;
BotScene::define('order')
->step('size')
->ask(fn ($ctx) => BotResponse::text('What size?')->keyboard( // pick by tapping
InlineKeyboard::make()
->button('Small', 'Small')->button('Medium', 'Medium')->button('Large', 'Large')
->toArray()))
->expectCallback() // read the tapped button
->rules(['
public function order(BotRequest $request)
{
return BotScene::enter('order'); // sends the first question
}
public function place(SceneContext $ctx) // onComplete handler — gets every answer
{
Order::create($ctx->all());
return BotResponse::view('order.done')->redirect('home');
}
use Wekser\Laragram\Telegram\Payments\Invoice;
use Wekser\Laragram\Telegram\Keyboards\InlineKeyboard;
// 1. Send a Stars invoice — chat_id is injected automatically
public function donate()
{
return $this->response->invoice(
Invoice::make()->title('Support us')->description('One-time donation')
->payload('donate_1')->stars(100, 'Donation')
)->keyboard(InlineKeyboard::make()->pay('Pay ⭐100')->toArray());
}
// Fiat variant: ->currency('USD')->price('Item', 1999)->providerToken(...)
// 2. Approve (or decline) the pre-checkout step — answer within 10 seconds
BotRoute::get('pre_checkout_query')->call(fn () => BotResponse::approveCheckout());
// 3. Handle the completed payment (a field on a "message" update — note the listener override)
BotRoute::get('message', 'successful_payment')->call([ShopController::class, 'paid']);
use Wekser\Laragram\Telegram\Inline\InlineResults;
BotRoute::get('inline_query')->call(function (BotRequest $request) {
return BotResponse::inlineResults(
InlineResults::make()
->article('1', 'Say hello', 'Hello there!') // sends text when picked
->photo('2', 'https://example.com/pic.jpg')
->cachedPhoto('3', $fileId) // by cached file_id
->cache(300)->personal()
);
});
// Optional: know which result the user picked (enable inline feedback with @BotFather)
BotRoute::get('chosen_inline_result')->call([StatsController::class, 'picked']);
use Wekser\Laragram\Services\MediaUploader;
public function send(MediaUploader $uploader, User $user): BotResponse
{
// Uploads to $chatId and returns the file_id — cache it, then reuse it for free.
$fileId = $uploader->upload('photo', storage_path('app/banner.jpg'), $user->uid);
$fileId = $uploader->upload('document', 'https://example.com/report.pdf', $user->uid);
return $this->response->photo($fileId, caption: 'Here you go');
}
use Wekser\Laragram\Services\MediaDownloader;
public function receive(BotRequest $request, MediaDownloader $downloader)
{
// Fluent handle off the request (null when the update carries no file):
$path = $request->file()?->save('local', 'inbox/receipt.jpg'); // store on a disk
$bytes = $request->file()?->bytes(); // raw bytes
// Or the service directly:
$path = $downloader->save($request->fileId(), 's3', 'kyc/doc.pdf');
return $this->response->text('Got it!');
}
// React to an incoming message (chat_id + message_id injected automatically)
BotRoute::get('message')->contains('/like')
->call(fn () => BotResponse::react('👍', big: true));
// Handle a user changing their reaction on a message
BotRoute::get('message_reaction')->call(function (BotRequest $request) {
$reaction = $request->messageReaction(); // chat, message_id, user, old/new_reaction
return BotResponse::react('❤️'); // react back on the same message
});
use Wekser\Laragram\Facades\BotBroadcast;
use Wekser\Laragram\Facades\BotResponse;
use Wekser\Laragram\Telegram\Keyboards\InlineKeyboard;
// Raw text to every active user
BotBroadcast::text('We are back online!')->send();
// A view, rendered per recipient (in their own language, with $user in scope)
BotBroadcast::view('news.release', ['version' => '2.0'])
->role(['admin', 'moderator']) // optional: restrict to roles
->
use Illuminate\Support\Facades\Event;
use Wekser\Laragram\Events\BotExceptionHandled;
Event::listen(function (BotExceptionHandled $e) {
// $e->exception, $e->reportable (was it logged?), $e->terminal (user unreachable?)
if ($e->terminal) {
// e.g. count how many users blocked the bot
}
// push to Sentry / StatsD / Horizon tags…
});
use Wekser\Laragram\Testing\BotUpdateFactory;
use Wekser\Laragram\Testing\InteractsWithBot;
class StartCommandTest extends TestCase
{
use InteractsWithBot;
public function test_start_greets_and_moves_to_home(): void
{
$this->botReceives(BotUpdateFactory::message('/start'));
$this->assertBotRepliedWith('sendMessage');
$this->assertBotRepliedText('Welcome');
$this->assertUserRedirectedTo('home');
}
public function test_order_wizard_collects_the_size(): void
{
$this->botReceives(BotUpdateFactory::message('/order'));
$this->botReceives(BotUpdateFactory::callbackQuery('Medium'));
$this->assertInScene('order');
$this->assertSceneData('size', 'Medium');
$this->assertSceneStep('address');
}
}
bash
php artisan laragram:broadcast "We are back online!" # text to all active users
php artisan laragram:broadcast --view=news.release # render a view per recipient
php artisan laragram:broadcast --view=news.release --data='{"version":"2.0"}' # + template data (JSON)
php artisan laragram:broadcast "Admins only" --role=admin --dry-run # just print the recipient count
bash
php artisan laragram:admin:create # prompts for username + password (min 8 chars)
php artisan laragram:admin:create alice --name="Alice" --password=secret123 # or non-interactively
php artisan laragram:admin:delete alice # remove an account