botasis / runtime
Bot运行时库,用于处理接收到的更新
Requires
- php: ^8.2
- botasis/telegram-client: ^1.0
- psr/container: ^2.0
- psr/event-dispatcher: ^1.0
- psr/log: ^1.0.0||^2.0.0||^3.0.0
- symfony/deprecation-contracts: ^3.5
- yiisoft/friendly-exception: ^1.1
- yiisoft/injector: ^1.0
Requires (Dev)
- maglnet/composer-require-checker: ^4.5
- phpunit/phpunit: ^10.1
- roave/infection-static-analysis-plugin: ^1.16
- symfony/console: ^6.2
- symfony/http-client: ^7.0
- vimeo/psalm: ^5.4
- yiisoft/definitions: ^3.3
- yiisoft/test-support: ^3.0
Suggests
- symfony/http-client: ^6.0
- yiisoft/yii-console: ^1.0
- yiisoft/yii-event: ^2.0
- dev-master / 1.0.x-dev
- 0.12.0
- 0.11.4
- 0.11.3
- 0.11.2
- 0.11.1
- 0.11.0
- 0.10.0
- 0.9.3
- 0.9.2
- 0.9.1
- 0.9.0
- 0.8
- 0.7.4
- 0.7.3
- 0.7.2
- 0.7.1
- 0.7.0
- 0.6.1
- 0.6.0
- 0.5.3
- 0.5.2
- 0.5.1
- 0.5.0
- 0.4.0
- 0.3.0
- 0.2.1
- 0.2.0
- 0.1.0
- dev-dependabot/composer/symfony/console-tw-7.0
- dev-improved-callables
- dev-dependabot/composer/symfony/http-client-tw-7.0
- dev-docs
- dev-move-to-botasis
- dev-improve-set-webhook
This package is auto-updated.
Last update: 2024-09-14 07:22:42 UTC
README
Botasis Runtime是一个强大且灵活的PHP库,旨在简化Telegram机器人应用程序的开发。它作为构建Telegram机器人的基础框架,提供必要的抽象和功能,使得创建交互式和智能聊天机器人变得前所未有的容易。
示例
以下是一个简单的Telegram机器人示例,它将根据您的请求发送货币汇率,但前提是您已经为此功能支付了费用。
- 创建路由
[ (new Group( // This bot will work in private chats only new RuleDynamic(static fn(Update $update) => $update->chat->type === ChatType::PRIVATE), ...[ '/start' => new Route( new RuleStatic('/start'), [StartAction::class, 'handle'], ), '/pay' => new Route( new RuleStatic('/pay'), [PayAction::class, 'handle'], ), 'rates requests' => (new Route( new RuleDynamic( static fn(Update $update): bool => preg_match('#^/rates \w+ \w+$#', $update->requestData ?? '') === 1, ), [RatesAction::class, 'handle'], ))->withMiddlewares(PaidAccessMiddleware::class), ], ))->withMiddlewares(UserRegisterMiddleware::class), ]
我们在这里将三个命令映射到相应的操作。/start
和/pay
命令是静态路由。这意味着只有当它们与用户发送的完全匹配时,才会被映射。
另一方面,/rates
命令是动态的,应该看起来像/rates USD GBP
。 - 创建命令处理程序。
- 最简单的一个是
StartAction
。它只会发送一条问候消息。final readonly class StartAction { public function handle(Update $update): ResponseInterface { return (new Response($update)) ->withRequest( new Message( // You should properly escape all special characters to send a markdown message 'Hello\\! Use \\/pay command to get premium\\, and then use \\/rates command ' . 'to see currency exchange rates\\. I\\.e\\. `/pay USD GBP`\\.', MessageFormat::MARKDOWN, $update->chat->id, ), ); } }
- 接受支付超出了本例的范围。我将跳过
PayAction
处理程序,因为它与RatesAction
相比没有有趣的内容。 RatesAction
处理程序final readonly class RatesAction { public function __construct(private readonly RatesService $ratesService) {} public function handle( Update $update, #[UpdateAttribute('user')] User $user, ): ResponseInterface { // User sent a request like "/rates USD GBP", so we need to get USD and GBP from the request [, $currency1, $currency2] = explode(' ', $update->requestData); $user->saveRequestHistory($currency1, $currency2); $rate = $this->ratesService->getRate($currency1, $currency2); $date = date('Y-m-d H:i:s'); // send a message as a response return (new Response($update)) ->withRequest( new Message( "1 $currency1 = $rate $currency2 on $date. To get a reverse rate, " . "use /rates $currency2 $currency1 command.", MessageFormat::TEXT, $update->chat->id, ), ); } }
- 最简单的一个是
- 创建中间件。
UserRegisterMiddleware
被分配到外部组。因此,它总是在每个处理程序之前执行。它注册一个用户并将其添加到Update对象中作为属性final class UserRegisterMiddleware implements MiddlewareInterface { public function __construct(private UserRepository $repository) { } public function process(Update $update, UpdateHandlerInterface $handler): ResponseInterface { // Repository either finds a user or creates a new one $holder = $this->repository->getUser($update->user, $update->chat->id); // now $update->getAttribute('user') contains a User object return $handler->handle($update->withAttribute('user', $holder)); } }
由于这个中间件,请求处理程序可以使用#[UpdateAttribute('user')]
属性并获取一个类型化的User
对象(请参阅RatesAction::handle()
)。PaidAccessMiddleware
不允许非高级用户进行汇率请求。它也准备好附加到任何其他付费端点。final class PaidAccessMiddleware implements MiddlewareInterface { public function process(Update $update, UpdateHandlerInterface $handler): ResponseInterface { /** @var User $user */ $user = $update->getAttribute('user'); if (!$user->isPremium()) { return (new Response($update)) ->withRequest(new Message( 'Only premium users can use this command', MessageFormat::TEXT, $update->chat->id, )); } return $handler->handle($update); } }
使用Botasis Runtime,您不必担心Telegram基础设施。只需像处理HTTP请求一样编写您的业务逻辑即可!
主要功能
-
中间件堆栈:Botasis Runtime提供了一个强大的中间件系统,允许您轻松定义和组织传入的Telegram更新的处理。通过中间件,您可以实现各种行为和逻辑,例如身份验证、消息预处理和后处理等。
-
路由和分发:该库包括一个灵活且高效的路由系统,使您能够为特定的Telegram命令或交互定义路由。路由组、内部中间件和路由参数捕获提供了对更新处理的精细控制。
-
更新处理:Botasis Runtime简化了Telegram更新的管理和操作。它提供了一个直观的API来访问和修改更新数据,使与用户交互和响应用户消息变得轻松。
-
框架无关性:Botasis Runtime设计为框架无关,这意味着它可以无缝集成到各种PHP应用程序和框架中。无论您是否使用特定的PHP框架或开发独立的机器人应用程序,Botasis Runtime都能适应您的项目需求。
-
可扩展性:通过添加您自己的中间件、操作和自定义逻辑来扩展和自定义您的Telegram机器人的行为。它具有您可能需要的所有扩展点。
-
配置:微调机器人设置、路由规则和中间件堆栈,创建一个完全符合您愿景的机器人。无需硬编码,一切均可配置。
-
可伸缩性:随着您的机器人成长和演变,Botasis Runtime使您能够轻松地进行更改和改进,确保您的机器人能够适应新的功能和用户交互。
-
支持最佳实践
- PSR启用。没有硬编码的依赖项。您可以使用任何PSR实现。
- SOLID代码。您一定会喜欢依赖倒置原则的实现,因为它允许您将任何东西传递到任何地方,并且可以轻松地切换实现。
- 支持长运行。它已准备好与RoadRunner、Swoole和其他长运行应用程序引擎一起使用。这意味着Botasis运行时没有任何有状态的服务,并且永远不会重复计算同一件事。
快速入门
使用Botasis开始您的应用程序最快的方法是使用Botasis应用程序模板。
如果您不想使用它,或者您想将Botasis嵌入到现有的应用程序中,请按照以下步骤操作
-
使用Composer安装Botasis运行时和所有依赖项
composer require botasis/runtime httpsoft/http-message php-http/socket-client yiisoft/event-dispatcher yiisoft/di
包详细信息
botasis/runtime
- 此包,必需httpsoft/http-message
- PSR-7 (HTTP Message) 和 PSR-17 (HTTP Factories) 的实现。您可以使用任何实现,但个人我更喜欢这个。php-http/socket-client
- PSR-18 (HTTP Client) 的实现。您可以使用任何实现。yiisoft/event-dispatcher
- PSR-14 (Event Dispatcher) 的实现。您可以使用任何实现,但个人我更喜欢这个,因为它是很好的且与框架无关的实现。yiisoft/di
- PSR-11 (DI Container) 的实现。您可以使用任何实现,但个人我更喜欢这个,因为它是非常高效、方便且与框架无关的实现。
-
创建一个新的PHP脚本来初始化您的机器人。通常DI容器会处理大部分这些工作。
PHP脚本列表
use Botasis\Client\Telegram\Client\ClientPsr; use Botasis\Runtime\Application; use Botasis\Runtime\CallableFactory; use Botasis\Runtime\Emitter; use Botasis\Runtime\Handler\DummyUpdateHandler; use Botasis\Runtime\Middleware\Implementation\RouterMiddleware; use Botasis\Runtime\Middleware\MiddlewareDispatcher; use Botasis\Runtime\Middleware\MiddlewareFactory; use Botasis\Runtime\Router\Route; use Botasis\Runtime\Router\Router; use Botasis\Runtime\Router\RuleStatic; use Botasis\Runtime\UpdateHandlerInterface; use Http\Client\Socket\Client; use HttpSoft\Message\RequestFactory; use HttpSoft\Message\StreamFactory; use Psr\Container\ContainerInterface; use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; use Yiisoft\Di\Container; use Yiisoft\EventDispatcher\Dispatcher\Dispatcher; /** * @var string $token - a bot token you've got from the BotFather * @var ClientInterface $httpClient - an HTTP client. If you've installed the php-http/socket-client package, * it's {@see Client}. Either it's a client of your choice. * @var RequestFactoryInterface $requestFactory - a PSR-17 HTTP request factory. If you've installed the httpsoft/http-message package, * it's {@see RequestFactory}. * @var StreamFactoryInterface $streamFactory - a PSR-17 HTTP stream factory. If you've installed the httpsoft/http-message package, * it's {@see StreamFactory}. * @var EventDispatcherInterface $eventDispatcher - a PSR-14 event dispatcher. If you've installed the yiisoft/event-dispatcher package, * it's {@see Dispatcher}. * @var ContainerInterface $container - a PST-11 DI container. If you've installed the yiisoft/di package, * it's {@see Container}. */ $client = new ClientPsr( $token, $httpClient, $requestFactory, $streamFactory, ); $emitter = new Emitter($client, $eventDispatcher); $middlewareDispatcher = new MiddlewareDispatcher( new MiddlewareFactory($container, new CallableFactory($container)), $eventDispatcher, ); /** * Routes definition. Here we define a route for the /start message. The HelloAction will be instantiated by a DI container. */ $routes = [ new Route(new RuleStatic('/start'), [HelloAction::class, 'handle']), ]; /** * Middlewares definition. {@see RouterMiddleware} should be the last one. */ $middlewares = [new RouterMiddleware(new Router($container, $middlewareDispatcher, ...$routes))]; $middlewareDispatcher = $middlewareDispatcher->withMiddlewares(); $application = new Application($emitter, new DummyUpdateHandler(), $middlewareDispatcher);
-
根据您的机器人的行为和需求,通过注册中间件、操作和路由来自定义您的机器人。
-
开始接收更新。您可以使用GetUpdatesCommand从Telegram API拉取更新(在本地工作时)或使用SetTelegramWebhookCommand设置您的机器人的webhook地址,这样Telegram会自己向您发送更新。
就这样!您现在已使用Botasis运行时为您的机器人设置了基础。您可以通过自定义操作、中间件和路由来增强机器人的功能,为您的用户提供引人入胜的交互式体验。
功能
1. 路由
您可以为您的高级机器人创建路由和子路由。每个路由由两部分组成;
- A
Rule
。当来自Telegram的Update
到来时,Router
检查它是否满足每个路由的规则。一旦找到这样的路由,其Action
就会被执行。有两种类型的规则-
RuleStatic
。它映射到Update中的消息或回调数据。当从Telegram收到消息或回调时,它与每个现有的RuleStatic
进行比较。创建此类规则非常简单:new RuleStatic('/command')
。只有在没有合适的静态规则时,我们才会进一步到RuleDynamic
列表。 -
RuleDynamic
。与RuleStatic
相反,这种规则类型对每个Update
执行可调用。此类规则的创建可能如下所示:new RuleDynamic(static fn(Update $update) => str_starts_with($update->requestData ?? '', '/start@myBot'))
。
此类可调用必须返回一个布尔值。可调用定义应遵循扩展可调用定义格式。
-
- 一个
操作
。它是一个可调用的,可以像RuleDynamic
可调用一样定义。但是路由操作的返回值 必须 是null
/void
或ResponseInterface
。在其他所有情况下,路由器将抛出异常。
1. 路由中属性的用法
1. 状态管理
当您的应用程序需要处理聊天或用户状态时,此功能至关重要。按照以下四个步骤使用它
- 实现 StateRepositoryInterface。
您可以使用任何现有的实现 (它们将在以后实现)。 - 使用存储库保存用户/聊天状态
final class CharacterNameCommandAction { public function __construct(private StateRepositoryInterface $repository) {} public function handle(Update $update): ResponseInterface { $state = new StateJson($update->user->id, $update->chat->id, 'setting-name'); $this->repository->save($state); return (new Response($update)) ->withRequest(new Message( 'Enter your character name below', MessageFormat::TEXT, $update->chat->id, )); } }
- 在路由器中间件之前添加 StateMiddleware。
这允许您在路由操作中访问当前状态。$state = $update->getAttribute(\Botasis\Runtime\State\StateMiddleware::class);
注意! 此中间件搜索用户和聊天 ID。如果您只需要用户或仅需要聊天 ID,您需要自己实现此逻辑。
- 在路由中使用状态
[ new Route(new RuleStatic('/set_name'), CharacterNameCommandAction::class), new Route( new RuleDynamic(static fn(Update $update) => $update->getAttributes(StateMiddleware::class)?->getData() === json_encode('setting-name')), CharacterNameSetAction::class, ), ]
如果您更喜欢创建严格类型的 State 对象,则需要实现 StateInterface 以及 StateRepositoryInterface。