快速构建你的第一个Telegram机器人
注册Telegram机器人
首先,在Telegram中联系 @BotFather。使用 /newbot 命令创建一个新的机器人,为其设置一个友好的名称以及一个以 bot 结尾的用户名。BotFather将提供一个唯一的API令牌——复制该令牌并妥善保存,用于下一步。
安装Nutgram
Nutgram是用于在Laravel中构建Telegram机器人的Composer包。前往该包的文档查看当前的安装命令,并将其引入你的项目。
接下来,发布包的配置文件:
1
|
php artisan vendor:publish --provider="Nutgram\Laravel\NutgramServiceProvider" --tag="nutgram"
|
并将令牌添加到你的环境文件中。在 .env 文件底部添加:
1
|
TELEGRAM_BOT_TOKEN=你的机器人API令牌
|
发布后,你会看到 config/nutgram.php 文件,其中包含令牌配置和一些有用的默认设置:安全模式检测、是否启用路由以及生成文件的存放位置。我喜欢这里的默认设置。
路由与Telegram路由文件
Nutgram提供了一个 routes/telegram.php 文件,你可以在其中声明机器人特定的端点,类似于路由文件或Artisan命令注册。例如,一个非常简单的开始命令如下所示:
1
2
3
4
5
6
7
|
<?php
// routes/telegram.php
use SergiX\Nutgram\Nutgram;
$bot->onCommand('start', function (Nutgram $bot) {
$bot->sendMessage('Hello world');
});
|
如果你切换回Telegram并发送 /start,除非你的机器人正在主动轮询或已配置Webhook,否则你不会看到任何响应。
本地开发的轮询模式
我在本地使用轮询模式。Nutgram暴露了Artisan命令来辅助此操作。打开终端并运行:
1
|
php artisan nutgram:listen
|
这将激活轮询,你的机器人将接收更新。切换回Telegram,发送 /start,你应该会收到“Hello world”的回复。
通配符、回退与简单命令
你可以像Laravel路由参数一样设置带通配符的命令。例如,一个接受名称的 greet 命令:
1
2
3
|
$bot->onCommand('greet {name}', function (Nutgram $bot, $name) {
$bot->sendMessage("What's up {$name}?");
});
|
如果用户只发送 /greet(没有名称),则不会匹配。为了提供更好的用户体验,可以添加一个回退命令:
1
2
3
|
$bot->fallback(function (Nutgram $bot) {
$bot->sendMessage('Sorry, not sure how to respond to this. Try /greet {name}.');
});
|
该回退命令将在收到无法识别的输入时触发,你可以建议一个命令或提供帮助文本。
一个实际示例:退款命令
假设你运营一个订阅网站,并希望有一个 /refund {email} 命令来取消某人的订阅。在机器人端保持简单,并将业务逻辑委托给你的应用:
1
2
3
4
5
6
7
8
9
10
11
|
$bot->onCommand('refund {email}', function (Nutgram $bot, string $email) {
$user = User::where('email', $email)->first();
if (! $user) {
return $bot->sendMessage('Could not find a user with that email.');
}
$user->refund();
return $bot->sendMessage("Refunded {$email} ✅");
});
|
命令类(Nutgram命令)
如果你的逻辑变得复杂,可以将其提取到Nutgram命令类中(类似于Artisan命令)。生成一个命令:
1
|
php artisan nutgram:make-command RefundCommand
|
然后将闭包逻辑移到命令的 handle 方法中,并在你的 routes/telegram.php 文件中注册该类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
<?php
namespace App\Telegram\Commands;
use App\Actions\Admin\RefundAndCancel;
use App\User;
use Exception;
use SergiX44\Nutgram\Handlers\Type\Command;
use SergiX44\Nutgram\Nutgram;
use function sprintf;
use function trim;
class RefundCommand extends Command
{
protected string $command = 'refund {email}';
protected ?string $description = 'Refund a user by email address.';
public function handle(Nutgram $bot, string $email, RefundAndCancel $action): void
{
$user = User::query()->firstWhere('email', trim($email));
if (! $user) {
$bot->sendMessage("❌ Could not find a user with email: {$email}");
return;
}
try {
$response = $action->handle($user);
$bot->sendMessage(sprintf("✅ Refunded %s\n\n%s", $email, $response['message']));
} catch (Exception $exception) {
$bot->sendMessage("❌ Unable to process refund: {$exception->getMessage()}");
}
}
}
// 在 routes/telegram.php 中注册
use App\Telegram\Commands\RefundCommand;
$bot->registerCommand(RefundCommand::class);
|
生产环境的Webhook
部署时,使用Webhook而不是轮询。其原理很简单:在你的API路由下注册一个路由,并让Telegram将更新发布到该路由。
API路由用于无状态端点;它们不使用会话或CSRF保护。
创建(或使用)routes/api.php 并添加:
1
2
3
4
5
6
7
8
9
10
11
|
<?php
// routes/api.php
use App\Http\Middleware\AuthorizeTelegramUser;
use SergiX44\Nutgram\Nutgram;
use Illuminate\Support\Facades\Route;
use SergiX44\Nutgram\RunningMode\Webhook;
Route::post('/telegram/webhook', function (Nutgram $bot) {
$bot->setRunningMode(Webhook::class);
$bot->run();
});
|
然后你需要将这个端点设置给Telegram。
务必运行此命令来“告诉”Telegram你的Webhook端点。
1
|
php artisan nutgram:hook:set https://telegram-demo/api/telegram/webhook
|
现在,Telegram将把更新推送到你的应用,而不是你的应用去轮询它们。
向Telegram注册命令
如果你希望用户在Telegram中输入 / 时看到自动补全,你必须将你的命令注册到Telegram服务器。Nutgram提供了一个辅助命令:
1
|
php artisan nutgram:register-commands
|
添加或更改命令后运行此命令,稍等片刻;命令应该会显示在Telegram的自动补全中。
授权(仅允许特定用户)
如果只有特定的Telegram用户才被允许运行敏感命令,可以检查传入的用户ID并阻止其他所有请求。一个快速发现你的Telegram用户ID的方法是创建一个像 /whoami 这样的小命令来报告 $bot->user()->id。
1
2
3
|
$bot->onCommand('whoami', function (Nutgram $bot) {
$bot->sendMessage('You seem to have a Telegram user id of '.$bot->user()->id);
})->description('Who am I command.');
|
一旦我们知道了相应的Telegram用户ID,我们就可以创建一个中间件来限制只允许该特定用户访问。
我通常为此创建中间件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<?php
// app/Http/Middleware/AuthorizeTelegramUser.php
use Closure;
use SergiX\Nutgram\Nutgram;
class AuthorizeTelegramUser
{
public function handle(Nutgram $bot, Closure $next)
{
$allowed = config('nutgram.authorized_user');
if ($bot->user()->id !== $allowed) {
$bot->sendMessage('You are unauthorized to use this bot.');
throw new \Illuminate\Auth\Access\AuthorizationException;
}
return $next($bot);
}
}
|
然后我们可以将这个中间件添加到Nutgram运行的管道中(查看包文档了解在哪里注册它)。不要硬编码ID;而是在 config/nutgram.php 中添加一个配置键:
1
2
|
// config/nutgram.php
'authorized_user' => env('TELEGRAM_USER_ID'),
|
并在你的 .env 中设置:
1
|
TELEGRAM_USER_ID=123456789
|
就是这样——实现起来非常容易,一旦你恰当地提取和组织逻辑,它将变得非常强大。