slack-php / slack-app-framework
提供在PHP中构建Slack应用的基础
Requires
- php: >=7.4
- ext-ctype: *
- ext-json: *
- nyholm/psr7: ^1.3
- nyholm/psr7-server: ^1.0
- psr/container: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
- psr/log: ^1.1
- slack-php/slack-block-kit: ^0.19.0 || ^1.0.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.18
- phpstan/phpstan: ^0.12.77
- phpunit/phpunit: ^9.5
README
PHP Slack应用框架
由Jeremy Lindblom (@jeremeamia)创作
简介
一个用于构建Slack应用的PHP框架。它从Slack的Bolt框架中汲取灵感。
如果你是Slack应用开发的初学者,你可以在Slack网站上了解它。这个库只有在您已经了解构建Slack应用的基础知识时才有用。
安装
- 需要PHP 7.4+
- 使用Composer安装:
composer require slack-php/slack-app-framework
一般用法
快速警告
这个库已经被大量内部测试,但项目严重缺乏测试覆盖率和文档,因此请自行承担风险,如MIT许可证所述。
开发模式
创建应用时,您可以从Slack网站配置您的应用。该框架旨在接收来自您的应用所有交互点的请求,因此您应将所有URL(例如,在斜杠命令、交互性与快捷键(不要忘记选择菜单部分)和事件订阅中)配置为指向已部署的应用代码的根URL。
在开发应用代码时,您可以使用App
的路线方法声明一个或多个Listener
,这些方法对应于不同类型的应用交互。可以将Listener
声明为闭包,或作为SlackPhp\Framework\Listener
类型的对象和类名。一个Listener
接收一个Context
对象,该对象包含Slack提供给应用的有效负载数据,并提供了所有可以采取的与Slack交互或通信的方法。
快速示例
这个小程序响应/cool
斜杠命令。
假设
- 您已要求Composer自动加载器启用框架文件的自动加载。
- 您已在环境中设置了
SLACK_SIGNING_KEY
(例如,putenv("SLACK_SIGNING_KEY=foo");
)
<?php use SlackPhp\Framework\App; use SlackPhp\Framework\Context; App::new() ->command('cool', function (Context $ctx) { $ctx->ack(':thumbsup: That is so cool!'); }) ->run();
示例应用程序
"Hello World"应用通过利用每种类型的应用交互,包括:斜杠命令、块操作、块建议(即菜单选项)、快捷键(全局和消息级别)、模态、事件和应用程序主页,向您问好。
"Hello World"应用代码
假设
- 您已要求Composer自动加载器启用框架文件的自动加载。
- 您已在环境中设置了
SLACK_SIGNING_KEY
(例如,putenv("SLACK_SIGNING_KEY=foo");
)- 您已在环境中设置了
SLACK_BOT_TOKEN
(例如,putenv("SLACK_BOT_TOKEN=bar");
)
<?php declare(strict_types=1); use SlackPhp\BlockKit\Surfaces\{Message, Modal}; use SlackPhp\Framework\{App, Context, Route}; // Helper for creating a modal with the "hello-form" for choosing a greeting. $createModal = function (): Modal { return Modal::new() ->title('Choose a Greeting') ->submit('Submit') ->callbackId('hello-form') ->notifyOnClose(true) ->tap(function (Modal $modal) { $modal->newInput('greeting-block') ->label('Which Greeting?') ->newSelectMenu('greeting') ->forExternalOptions() ->placeholder('Choose a greeting...'); }); }; App::new() // Handles the `/hello` slash command. ->command('hello', function (Context $ctx) { $ctx->ack(Message::new()->tap(function (Message $msg) { $msg->newSection() ->mrkdwnText(':wave: Hello world!') ->newButtonAccessory('open-form') ->text('Choose a Greeting'); })); }) // Handles the "open-form" button click. ->blockAction('open-form', function (Context $ctx) use ($createModal) { $ctx->modals()->open($createModal()); }) // Handles when the "greeting" select menu needs its options. ->blockSuggestion('greeting', function (Context $ctx) { $ctx->options(['Hello', 'Howdy', 'Good Morning', 'Hey']); }) // Handles when the "hello-form" modal is submitted. ->viewSubmission('hello-form', function (Context $ctx) { $state = $ctx->payload()->getState(); $greeting = $state->get('greeting-block.greeting.selected_option.value'); $ctx->view()->update(":wave: {$greeting} world!"); }) // Handles when the "hello-form" modal is closed without submitting. ->viewClosed('hello-form', function (Context $ctx) { $ctx->logger()->notice('User closed hello-form modal early.'); }) // Handles when the "hello-global" global shortcut is triggered from the lightning menu. ->globalShortcut('hello-global', function (Context $ctx) use ($createModal) { $ctx->modals()->open($createModal()); }) // Handles when the "hello-message" message shortcut is triggered from a message context menu. ->messageShortcut('hello-message', function (Context $ctx) { $user = $ctx->fmt()->user($ctx->payload()->get('message.user')); $ctx->say(":wave: Hello {$user}!", null, $ctx->payload()->get('message.ts')); }) // Handles when the Hello World app "home" is accessed. ->event('app_home_opened', function (Context $ctx) { $user = $ctx->fmt()->user($ctx->payload()->get('event.user')); $ctx->home(":wave: Hello {$user}!"); }) // Handles when any public message contains the word "hello". ->event('message', Route::filter( ['event.channel_type' => 'channel', 'event.text' => 'regex:/^.*hello.*$/i'], function (Context $ctx) { $user = $ctx->fmt()->user($ctx->payload()->get('event.user')); $ctx->say(":wave: Hello {$user}!"); }) ) // Run that app to process the incoming Slack request. ->run();
面向对象版本
您还可以将您的App和Listeners作为一组类创建。如果您有多个监听器或监听器很复杂,我建议您采取这种方法。以下是"Hello World"应用以这种方式开发的示例。
"Hello World"应用代码
App.php
<?php declare(strict_types=1); namespace MyApp; use SlackPhp\Framework\{BaseApp, Route, Router}; use MyApp\Listeners; class MyCoolApp extends BaseApp { protected function prepareRouter(Router $router): void { $router->command('hello', Listeners\HelloCommand::class) ->blockAction('open-form', Listeners\OpenFormButtonClick::class) ->blockSuggestion('greeting', Listeners\GreetingOptions::class) ->viewSubmission('hello-form', Listeners\FormSubmission::class) ->viewClosed('hello-form', Listeners\FormClosed::class) ->globalShortcut('hello-global', Listeners\HelloGlobalShortcut::class) ->messageShortcut('hello-message', Listeners\HelloMessageShortcut::class) ->event('app_home_opened', Listeners\AppHome::class) ->event('message', Route::filter( ['event.channel_type' => 'channel', 'event.text' => 'regex:/^.*hello.*$/i'], Listeners\HelloMessage::class )); } }
index.php
假设
- 您已要求Composer自动加载器启用框架文件的自动加载。
- 您已配置composer.json,以便自动加载您的
MyApp
命名空间代码。- 您已在环境中设置了
SLACK_SIGNING_KEY
(例如,putenv("SLACK_SIGNING_KEY=foo");
)- 您已在环境中设置了
SLACK_BOT_TOKEN
(例如,putenv("SLACK_BOT_TOKEN=bar");
)
<?php use MyApp\MyCoolApp; $app = new MyCoolApp(); $app->run();
使用Context
对象处理请求
Context
对象是您的应用与Slack之间交互的主要点。以下是您可以使用Context
执行的所有操作
// To respond (ack) to incoming Slack request:
$ctx->ack(Message|array|string|null) // Responds to request with 200 (and optional message)
$ctx->options(OptionList|array|null) // Responds to request with an options list
$ctx->view(): View
->clear() // Responds to modal submission by clearing modal stack
->close() // Responds to modal submission by clearing current modal
->errors(array) // Responds to modal submission by providing form errors
->push(Modal|array|string) // Responds to modal submission by pushing new modal to stack
->update(Modal|array|string) // Responds to modal submission by updating current modal
// To call Slack APIs (to send messages, open/update modals, etc.) after the ack:
$ctx->respond(Message|array|string) // Responds to message. Uses payload.response_url
$ctx->say(Message|array|string) // Responds in channel. Uses API and payload.channel.id
$ctx->modals(): Modals
->open(Modal|array|string) // Opens a modal. Uses API and payload.trigger_id
->push(Modal|array|string) // Pushes a new modal. Uses API and payload.trigger_id
->update(Modal|array|string) // Updates a modal. Uses API and payload.view.id
$ctx->home(AppHome|array|string) // Modifies App Home for user. Uses API and payload.user.id
$ctx->api(string $api, array $params) // Use Slack API client for arbitrary API operations
// Access payload or other contextual data:
$ctx->payload(): Payload // Returns the payload of the incoming request from Slack
$ctx->getAppId(): ?string // Gets the app ID, if it's known
$ctx->get(string): mixed // Gets a value from the context
$ctx->set(string, mixed) // Sets a value in the context
$ctx->isAcknowledged(): bool // Returns true if ack has been sent
$ctx->isDeferred(): bool // Returns true if additional processing will happen after the ack
// Access additional helpers:
$ctx->blocks(): Blocks // Returns a helper for creating Block Kit surfaces
$ctx->fmt(): Formatter // Returns the "mrkdwn" formatting helper for Block Kit text
$ctx->logger(): LoggerInterface // Returns an instance of the configured PSR-3 logger
$ctx->container(): ContainerInterface // Returns an instance of the configured PSR-11 container
高级设计
UML 源文件
[AppServer]<>-runs>[App] [AppServer]creates->[Context] [App]<>->[AppConfig] [App]<>->[Router] [Router]-^[Listener] [Router]<>1-*>[Listener] [Listener]handles->[Context] [Context]<>->[Payload] [Context]<>->[AppConfig] [Context]<>->[_Clients_;RespondClient;ApiClient] [Context]<>->[_Helpers_;BlockKit;Modals;View] [Context]<>->[_Metadata_] [AppConfig]<>->[Logger] [AppConfig]<>->[Container] [AppConfig]<>->[_Credentials_]
套接字模式
套接字模式由单独的包提供支持。请参阅slack-php/slack-php-socket-mode。
未实现
以下特性已知缺失
- 处理安装到不同工作区的 OAuth 流程。
- 尽管在
SlackPhp\Framework\Auth
命名空间中有些类,但如果你现在需要自定义。
- 尽管在
使用的标准
- PSR-1, PSR-12: 编码风格
- PSR-3: 日志接口
- PSR-4: 自动加载
- PSR-7, PSR-15, PSR-17: HTTP
- PSR-11: 容器接口